roojs-ui-debug.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          *   Deep object/array copy. Function clones are actually wrappers around the
125          *   original function. Array-like objects are treated as arrays. Primitives are
126          *   returned untouched. Optionally, a function can be provided to handle other data
127          *   types, filter keys, validate values, etc.
128          *
129          *   **Note:** Cloning a non-trivial object is a reasonably heavy operation, due to
130          *   the need to recursively iterate down non-primitive properties. Clone should be
131          *   used only when a deep clone down to leaf level properties is explicitly
132          *   required. This method will also
133          *
134             In many cases (for example, when trying to isolate objects used as hashes for
135             configuration properties), a shallow copy, using `Y.merge()` is normally
136             sufficient. If more than one level of isolation is required, `Y.merge()` can be
137             used selectively at each level which needs to be isolated from the original
138             without going all the way to leaf properties.
139
140             @method clone
141             @param {object} o what to clone.
142             @param {boolean} safe if true, objects will not have prototype items from the
143                 source. If false, they will. In this case, the original is initially
144                 protected, but the clone is not completely immune from changes to the source
145                 object prototype. Also, cloned prototype items that are deleted from the
146                 clone will result in the value of the source prototype being exposed. If
147                 operating on a non-safe clone, items should be nulled out rather than
148                 deleted.
149             @param {function} f optional function to apply to each item in a collection; it
150                 will be executed prior to applying the value to the new object.
151                 Return false to prevent the copy.
152             @param {object} c optional execution context for f.
153             @param {object} owner Owner object passed when clone is iterating an object.
154                 Used to set up context for cloned functions.
155             @param {object} cloned hash of previously cloned objects to avoid multiple
156                 clones.
157             @return {Array|Object} the cloned object.
158           **/
159         clone : function(o, safe, f, c, owner, cloned) {
160             var o2, marked, stamp;
161
162             // Does not attempt to clone:
163             //
164             // * Non-typeof-object values, "primitive" values don't need cloning.
165             //
166             // * YUI instances, cloning complex object like YUI instances is not
167             //   advised, this is like cloning the world.
168             //
169             // * DOM nodes (#2528250), common host objects like DOM nodes cannot be
170             //   "subclassed" in Firefox and old versions of IE. Trying to use
171             //   `Object.create()` or `Y.extend()` on a DOM node will throw an error in
172             //   these browsers.
173             //
174             // Instad, the passed-in `o` will be return as-is when it matches one of the
175             // above criteria.
176 //            if (!L.isObject(o) ||
177 //                    Y.instanceOf(o, YUI) ||
178 //                    (o.addEventListener || o.attachEvent)) {
179 //
180 //                return o;
181 //            }
182
183             marked = cloned || {};
184
185             switch (L.type(o)) {
186                 case 'date':
187                     return new Date(o);
188                 case 'regexp':
189                     // if we do this we need to set the flags too
190                     // return new RegExp(o.source);
191                     return o;
192                 case 'function':
193                     // o2 = Y.bind(o, owner);
194                     // break;
195                     return o;
196                 case 'array':
197                     o2 = [];
198                     break;
199                 default:
200                     
201                     // #2528250 only one clone of a given object should be created.
202                     if (o['_~roo~_']) {
203                         return marked[o['_~roo~_']];
204                     }
205
206                     stamp = Y.guid();
207
208                     o2 = (safe) ? {} : Y.Object(o);
209
210                     o['_~roo~_'] = stamp;
211                     marked[stamp] = o;
212             }
213
214             Roo.each(o, function(v, k) {
215                 if ((k || k === 0) && (!f || (f.call(c || this, v, k, this, o) !== false))) {
216                     if (k !== '_~roo~_') {
217                         if (k == 'prototype') {
218                             // skip the prototype
219                         // } else if (o[k] === o) {
220                         //     this[k] = this;
221                         } else {
222                             this[k] =
223                                 Roo.clone(v, safe, f, c, owner || o, marked);
224                         }
225                     }
226                 }
227             }, o2);
228
229             if (!cloned) {
230                 Roo.Object.each(marked, function(v, k) {
231                     if (v['_~roo~_']) {
232                         try {
233                             delete v['_~roo~_'];
234                         } catch (e) {
235                             v['_~roo~_'] = null;
236                         }
237                     }
238                 }, this);
239                 marked = null;
240             }
241
242             return o2;
243         },
244         /**
245          * Copies all the properties of config to obj if they don't already exist.
246          * @param {Object} obj The receiver of the properties
247          * @param {Object} config The source of the properties
248          * @return {Object} returns obj
249          */
250         applyIf : function(o, c){
251             if(o && c){
252                 for(var p in c){
253                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
254                 }
255             }
256             return o;
257         },
258
259         /**
260          * Applies event listeners to elements by selectors when the document is ready.
261          * The event name is specified with an @ suffix.
262 <pre><code>
263 Roo.addBehaviors({
264    // add a listener for click on all anchors in element with id foo
265    '#foo a@click' : function(e, t){
266        // do something
267    },
268
269    // add the same listener to multiple selectors (separated by comma BEFORE the @)
270    '#foo a, #bar span.some-class@mouseover' : function(){
271        // do something
272    }
273 });
274 </code></pre>
275          * @param {Object} obj The list of behaviors to apply
276          */
277         addBehaviors : function(o){
278             if(!Roo.isReady){
279                 Roo.onReady(function(){
280                     Roo.addBehaviors(o);
281                 });
282                 return;
283             }
284             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
285             for(var b in o){
286                 var parts = b.split('@');
287                 if(parts[1]){ // for Object prototype breakers
288                     var s = parts[0];
289                     if(!cache[s]){
290                         cache[s] = Roo.select(s);
291                     }
292                     cache[s].on(parts[1], o[b]);
293                 }
294             }
295             cache = null;
296         },
297
298         /**
299          * Generates unique ids. If the element already has an id, it is unchanged
300          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
301          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
302          * @return {String} The generated Id.
303          */
304         id : function(el, prefix){
305             prefix = prefix || "roo-gen";
306             el = Roo.getDom(el);
307             var id = prefix + (++idSeed);
308             return el ? (el.id ? el.id : (el.id = id)) : id;
309         },
310          
311        
312         /**
313          * Extends one class with another class and optionally overrides members with the passed literal. This class
314          * also adds the function "override()" to the class that can be used to override
315          * members on an instance.
316          * @param {Object} subclass The class inheriting the functionality
317          * @param {Object} superclass The class being extended
318          * @param {Object} overrides (optional) A literal with members
319          * @method extend
320          */
321         extend : function(){
322             // inline overrides
323             var io = function(o){
324                 for(var m in o){
325                     this[m] = o[m];
326                 }
327             };
328             return function(sb, sp, overrides){
329                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
330                     overrides = sp;
331                     sp = sb;
332                     sb = function(){sp.apply(this, arguments);};
333                 }
334                 var F = function(){}, sbp, spp = sp.prototype;
335                 F.prototype = spp;
336                 sbp = sb.prototype = new F();
337                 sbp.constructor=sb;
338                 sb.superclass=spp;
339                 
340                 if(spp.constructor == Object.prototype.constructor){
341                     spp.constructor=sp;
342                    
343                 }
344                 
345                 sb.override = function(o){
346                     Roo.override(sb, o);
347                 };
348                 sbp.override = io;
349                 Roo.override(sb, overrides);
350                 return sb;
351             };
352         }(),
353
354         /**
355          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
356          * Usage:<pre><code>
357 Roo.override(MyClass, {
358     newMethod1: function(){
359         // etc.
360     },
361     newMethod2: function(foo){
362         // etc.
363     }
364 });
365  </code></pre>
366          * @param {Object} origclass The class to override
367          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
368          * containing one or more methods.
369          * @method override
370          */
371         override : function(origclass, overrides){
372             if(overrides){
373                 var p = origclass.prototype;
374                 for(var method in overrides){
375                     p[method] = overrides[method];
376                 }
377             }
378         },
379         /**
380          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
381          * <pre><code>
382 Roo.namespace('Company', 'Company.data');
383 Company.Widget = function() { ... }
384 Company.data.CustomStore = function(config) { ... }
385 </code></pre>
386          * @param {String} namespace1
387          * @param {String} namespace2
388          * @param {String} etc
389          * @method namespace
390          */
391         namespace : function(){
392             var a=arguments, o=null, i, j, d, rt;
393             for (i=0; i<a.length; ++i) {
394                 d=a[i].split(".");
395                 rt = d[0];
396                 /** eval:var:o */
397                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
398                 for (j=1; j<d.length; ++j) {
399                     o[d[j]]=o[d[j]] || {};
400                     o=o[d[j]];
401                 }
402             }
403         },
404         /**
405          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
406          * <pre><code>
407 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
408 Roo.factory(conf, Roo.data);
409 </code></pre>
410          * @param {String} classname
411          * @param {String} namespace (optional)
412          * @method factory
413          */
414          
415         factory : function(c, ns)
416         {
417             // no xtype, no ns or c.xns - or forced off by c.xns
418             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
419                 return c;
420             }
421             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
422             if (c.constructor == ns[c.xtype]) {// already created...
423                 return c;
424             }
425             if (ns[c.xtype]) {
426                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
427                 var ret = new ns[c.xtype](c);
428                 ret.xns = false;
429                 return ret;
430             }
431             c.xns = false; // prevent recursion..
432             return c;
433         },
434          /**
435          * Logs to console if it can.
436          *
437          * @param {String|Object} string
438          * @method log
439          */
440         log : function(s)
441         {
442             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
443                 return; // alerT?
444             }
445             console.log(s);
446             
447         },
448         /**
449          * 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.
450          * @param {Object} o
451          * @return {String}
452          */
453         urlEncode : function(o){
454             if(!o){
455                 return "";
456             }
457             var buf = [];
458             for(var key in o){
459                 var ov = o[key], k = Roo.encodeURIComponent(key);
460                 var type = typeof ov;
461                 if(type == 'undefined'){
462                     buf.push(k, "=&");
463                 }else if(type != "function" && type != "object"){
464                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
465                 }else if(ov instanceof Array){
466                     if (ov.length) {
467                             for(var i = 0, len = ov.length; i < len; i++) {
468                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
469                             }
470                         } else {
471                             buf.push(k, "=&");
472                         }
473                 }
474             }
475             buf.pop();
476             return buf.join("");
477         },
478          /**
479          * Safe version of encodeURIComponent
480          * @param {String} data 
481          * @return {String} 
482          */
483         
484         encodeURIComponent : function (data)
485         {
486             try {
487                 return encodeURIComponent(data);
488             } catch(e) {} // should be an uri encode error.
489             
490             if (data == '' || data == null){
491                return '';
492             }
493             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
494             function nibble_to_hex(nibble){
495                 var chars = '0123456789ABCDEF';
496                 return chars.charAt(nibble);
497             }
498             data = data.toString();
499             var buffer = '';
500             for(var i=0; i<data.length; i++){
501                 var c = data.charCodeAt(i);
502                 var bs = new Array();
503                 if (c > 0x10000){
504                         // 4 bytes
505                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
506                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
507                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
508                     bs[3] = 0x80 | (c & 0x3F);
509                 }else if (c > 0x800){
510                          // 3 bytes
511                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
512                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
513                     bs[2] = 0x80 | (c & 0x3F);
514                 }else if (c > 0x80){
515                        // 2 bytes
516                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
517                     bs[1] = 0x80 | (c & 0x3F);
518                 }else{
519                         // 1 byte
520                     bs[0] = c;
521                 }
522                 for(var j=0; j<bs.length; j++){
523                     var b = bs[j];
524                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
525                             + nibble_to_hex(b &0x0F);
526                     buffer += '%'+hex;
527                }
528             }
529             return buffer;    
530              
531         },
532
533         /**
534          * 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]}.
535          * @param {String} string
536          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
537          * @return {Object} A literal with members
538          */
539         urlDecode : function(string, overwrite){
540             if(!string || !string.length){
541                 return {};
542             }
543             var obj = {};
544             var pairs = string.split('&');
545             var pair, name, value;
546             for(var i = 0, len = pairs.length; i < len; i++){
547                 pair = pairs[i].split('=');
548                 name = decodeURIComponent(pair[0]);
549                 value = decodeURIComponent(pair[1]);
550                 if(overwrite !== true){
551                     if(typeof obj[name] == "undefined"){
552                         obj[name] = value;
553                     }else if(typeof obj[name] == "string"){
554                         obj[name] = [obj[name]];
555                         obj[name].push(value);
556                     }else{
557                         obj[name].push(value);
558                     }
559                 }else{
560                     obj[name] = value;
561                 }
562             }
563             return obj;
564         },
565
566         /**
567          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
568          * passed array is not really an array, your function is called once with it.
569          * The supplied function is called with (Object item, Number index, Array allItems).
570          * @param {Array/NodeList/Mixed} array
571          * @param {Function} fn
572          * @param {Object} scope
573          */
574         each : function(array, fn, scope){
575             if(typeof array.length == "undefined" || typeof array == "string"){
576                 array = [array];
577             }
578             for(var i = 0, len = array.length; i < len; i++){
579                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
580             }
581         },
582
583         // deprecated
584         combine : function(){
585             var as = arguments, l = as.length, r = [];
586             for(var i = 0; i < l; i++){
587                 var a = as[i];
588                 if(a instanceof Array){
589                     r = r.concat(a);
590                 }else if(a.length !== undefined && !a.substr){
591                     r = r.concat(Array.prototype.slice.call(a, 0));
592                 }else{
593                     r.push(a);
594                 }
595             }
596             return r;
597         },
598
599         /**
600          * Escapes the passed string for use in a regular expression
601          * @param {String} str
602          * @return {String}
603          */
604         escapeRe : function(s) {
605             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
606         },
607
608         // internal
609         callback : function(cb, scope, args, delay){
610             if(typeof cb == "function"){
611                 if(delay){
612                     cb.defer(delay, scope, args || []);
613                 }else{
614                     cb.apply(scope, args || []);
615                 }
616             }
617         },
618
619         /**
620          * Return the dom node for the passed string (id), dom node, or Roo.Element
621          * @param {String/HTMLElement/Roo.Element} el
622          * @return HTMLElement
623          */
624         getDom : function(el){
625             if(!el){
626                 return null;
627             }
628             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
629         },
630
631         /**
632         * Shorthand for {@link Roo.ComponentMgr#get}
633         * @param {String} id
634         * @return Roo.Component
635         */
636         getCmp : function(id){
637             return Roo.ComponentMgr.get(id);
638         },
639          
640         num : function(v, defaultValue){
641             if(typeof v != 'number'){
642                 return defaultValue;
643             }
644             return v;
645         },
646
647         destroy : function(){
648             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
649                 var as = a[i];
650                 if(as){
651                     if(as.dom){
652                         as.removeAllListeners();
653                         as.remove();
654                         continue;
655                     }
656                     if(typeof as.purgeListeners == 'function'){
657                         as.purgeListeners();
658                     }
659                     if(typeof as.destroy == 'function'){
660                         as.destroy();
661                     }
662                 }
663             }
664         },
665
666         // inpired by a similar function in mootools library
667         /**
668          * Returns the type of object that is passed in. If the object passed in is null or undefined it
669          * return false otherwise it returns one of the following values:<ul>
670          * <li><b>string</b>: If the object passed is a string</li>
671          * <li><b>number</b>: If the object passed is a number</li>
672          * <li><b>boolean</b>: If the object passed is a boolean value</li>
673          * <li><b>function</b>: If the object passed is a function reference</li>
674          * <li><b>object</b>: If the object passed is an object</li>
675          * <li><b>array</b>: If the object passed is an array</li>
676          * <li><b>regexp</b>: If the object passed is a regular expression</li>
677          * <li><b>element</b>: If the object passed is a DOM Element</li>
678          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
679          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
680          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
681          * @param {Mixed} object
682          * @return {String}
683          */
684         type : function(o){
685             if(o === undefined || o === null){
686                 return false;
687             }
688             if(o.htmlElement){
689                 return 'element';
690             }
691             var t = typeof o;
692             if(t == 'object' && o.nodeName) {
693                 switch(o.nodeType) {
694                     case 1: return 'element';
695                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
696                 }
697             }
698             if(t == 'object' || t == 'function') {
699                 switch(o.constructor) {
700                     case Array: return 'array';
701                     case RegExp: return 'regexp';
702                 }
703                 if(typeof o.length == 'number' && typeof o.item == 'function') {
704                     return 'nodelist';
705                 }
706             }
707             return t;
708         },
709
710         /**
711          * Returns true if the passed value is null, undefined or an empty string (optional).
712          * @param {Mixed} value The value to test
713          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
714          * @return {Boolean}
715          */
716         isEmpty : function(v, allowBlank){
717             return v === null || v === undefined || (!allowBlank ? v === '' : false);
718         },
719         
720         /** @type Boolean */
721         isOpera : isOpera,
722         /** @type Boolean */
723         isSafari : isSafari,
724         /** @type Boolean */
725         isIE : isIE,
726         /** @type Boolean */
727         isIE7 : isIE7,
728         /** @type Boolean */
729         isGecko : isGecko,
730         /** @type Boolean */
731         isBorderBox : isBorderBox,
732         /** @type Boolean */
733         isWindows : isWindows,
734         /** @type Boolean */
735         isLinux : isLinux,
736         /** @type Boolean */
737         isMac : isMac,
738
739         /**
740          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
741          * you may want to set this to true.
742          * @type Boolean
743          */
744         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
745         
746         
747                 
748         /**
749          * Selects a single element as a Roo Element
750          * This is about as close as you can get to jQuery's $('do crazy stuff')
751          * @param {String} selector The selector/xpath query
752          * @param {Node} root (optional) The start of the query (defaults to document).
753          * @return {Roo.Element}
754          */
755         selectNode : function(selector, root) 
756         {
757             var node = Roo.DomQuery.selectNode(selector,root);
758             return node ? Roo.get(node) : new Roo.Element(false);
759         }
760         
761     });
762
763
764 })();
765
766 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
767                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
768 /*
769  * Based on:
770  * Ext JS Library 1.1.1
771  * Copyright(c) 2006-2007, Ext JS, LLC.
772  *
773  * Originally Released Under LGPL - original licence link has changed is not relivant.
774  *
775  * Fork - LGPL
776  * <script type="text/javascript">
777  */
778
779 (function() {    
780     // wrappedn so fnCleanup is not in global scope...
781     if(Roo.isIE) {
782         function fnCleanUp() {
783             var p = Function.prototype;
784             delete p.createSequence;
785             delete p.defer;
786             delete p.createDelegate;
787             delete p.createCallback;
788             delete p.createInterceptor;
789
790             window.detachEvent("onunload", fnCleanUp);
791         }
792         window.attachEvent("onunload", fnCleanUp);
793     }
794 })();
795
796
797 /**
798  * @class Function
799  * These functions are available on every Function object (any JavaScript function).
800  */
801 Roo.apply(Function.prototype, {
802      /**
803      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
804      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
805      * Will create a function that is bound to those 2 args.
806      * @return {Function} The new function
807     */
808     createCallback : function(/*args...*/){
809         // make args available, in function below
810         var args = arguments;
811         var method = this;
812         return function() {
813             return method.apply(window, args);
814         };
815     },
816
817     /**
818      * Creates a delegate (callback) that sets the scope to obj.
819      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
820      * Will create a function that is automatically scoped to this.
821      * @param {Object} obj (optional) The object for which the scope is set
822      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
823      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
824      *                                             if a number the args are inserted at the specified position
825      * @return {Function} The new function
826      */
827     createDelegate : function(obj, args, appendArgs){
828         var method = this;
829         return function() {
830             var callArgs = args || arguments;
831             if(appendArgs === true){
832                 callArgs = Array.prototype.slice.call(arguments, 0);
833                 callArgs = callArgs.concat(args);
834             }else if(typeof appendArgs == "number"){
835                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
836                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
837                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
838             }
839             return method.apply(obj || window, callArgs);
840         };
841     },
842
843     /**
844      * Calls this function after the number of millseconds specified.
845      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
846      * @param {Object} obj (optional) The object for which the scope is set
847      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
848      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
849      *                                             if a number the args are inserted at the specified position
850      * @return {Number} The timeout id that can be used with clearTimeout
851      */
852     defer : function(millis, obj, args, appendArgs){
853         var fn = this.createDelegate(obj, args, appendArgs);
854         if(millis){
855             return setTimeout(fn, millis);
856         }
857         fn();
858         return 0;
859     },
860     /**
861      * Create a combined function call sequence of the original function + the passed function.
862      * The resulting function returns the results of the original function.
863      * The passed fcn is called with the parameters of the original function
864      * @param {Function} fcn The function to sequence
865      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
866      * @return {Function} The new function
867      */
868     createSequence : function(fcn, scope){
869         if(typeof fcn != "function"){
870             return this;
871         }
872         var method = this;
873         return function() {
874             var retval = method.apply(this || window, arguments);
875             fcn.apply(scope || this || window, arguments);
876             return retval;
877         };
878     },
879
880     /**
881      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
882      * The resulting function returns the results of the original function.
883      * The passed fcn is called with the parameters of the original function.
884      * @addon
885      * @param {Function} fcn The function to call before the original
886      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
887      * @return {Function} The new function
888      */
889     createInterceptor : function(fcn, scope){
890         if(typeof fcn != "function"){
891             return this;
892         }
893         var method = this;
894         return function() {
895             fcn.target = this;
896             fcn.method = method;
897             if(fcn.apply(scope || this || window, arguments) === false){
898                 return;
899             }
900             return method.apply(this || window, arguments);
901         };
902     }
903 });
904 /*
905  * Based on:
906  * Ext JS Library 1.1.1
907  * Copyright(c) 2006-2007, Ext JS, LLC.
908  *
909  * Originally Released Under LGPL - original licence link has changed is not relivant.
910  *
911  * Fork - LGPL
912  * <script type="text/javascript">
913  */
914
915 Roo.applyIf(String, {
916     
917     /** @scope String */
918     
919     /**
920      * Escapes the passed string for ' and \
921      * @param {String} string The string to escape
922      * @return {String} The escaped string
923      * @static
924      */
925     escape : function(string) {
926         return string.replace(/('|\\)/g, "\\$1");
927     },
928
929     /**
930      * Pads the left side of a string with a specified character.  This is especially useful
931      * for normalizing number and date strings.  Example usage:
932      * <pre><code>
933 var s = String.leftPad('123', 5, '0');
934 // s now contains the string: '00123'
935 </code></pre>
936      * @param {String} string The original string
937      * @param {Number} size The total length of the output string
938      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
939      * @return {String} The padded string
940      * @static
941      */
942     leftPad : function (val, size, ch) {
943         var result = new String(val);
944         if(ch === null || ch === undefined || ch === '') {
945             ch = " ";
946         }
947         while (result.length < size) {
948             result = ch + result;
949         }
950         return result;
951     },
952
953     /**
954      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
955      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
956      * <pre><code>
957 var cls = 'my-class', text = 'Some text';
958 var s = String.format('<div class="{0}">{1}</div>', cls, text);
959 // s now contains the string: '<div class="my-class">Some text</div>'
960 </code></pre>
961      * @param {String} string The tokenized string to be formatted
962      * @param {String} value1 The value to replace token {0}
963      * @param {String} value2 Etc...
964      * @return {String} The formatted string
965      * @static
966      */
967     format : function(format){
968         var args = Array.prototype.slice.call(arguments, 1);
969         return format.replace(/\{(\d+)\}/g, function(m, i){
970             return Roo.util.Format.htmlEncode(args[i]);
971         });
972     }
973 });
974
975 /**
976  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
977  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
978  * they are already different, the first value passed in is returned.  Note that this method returns the new value
979  * but does not change the current string.
980  * <pre><code>
981 // alternate sort directions
982 sort = sort.toggle('ASC', 'DESC');
983
984 // instead of conditional logic:
985 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
986 </code></pre>
987  * @param {String} value The value to compare to the current string
988  * @param {String} other The new value to use if the string already equals the first value passed in
989  * @return {String} The new value
990  */
991  
992 String.prototype.toggle = function(value, other){
993     return this == value ? other : value;
994 };/*
995  * Based on:
996  * Ext JS Library 1.1.1
997  * Copyright(c) 2006-2007, Ext JS, LLC.
998  *
999  * Originally Released Under LGPL - original licence link has changed is not relivant.
1000  *
1001  * Fork - LGPL
1002  * <script type="text/javascript">
1003  */
1004
1005  /**
1006  * @class Number
1007  */
1008 Roo.applyIf(Number.prototype, {
1009     /**
1010      * Checks whether or not the current number is within a desired range.  If the number is already within the
1011      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
1012      * exceeded.  Note that this method returns the constrained value but does not change the current number.
1013      * @param {Number} min The minimum number in the range
1014      * @param {Number} max The maximum number in the range
1015      * @return {Number} The constrained value if outside the range, otherwise the current value
1016      */
1017     constrain : function(min, max){
1018         return Math.min(Math.max(this, min), max);
1019     }
1020 });/*
1021  * Based on:
1022  * Ext JS Library 1.1.1
1023  * Copyright(c) 2006-2007, Ext JS, LLC.
1024  *
1025  * Originally Released Under LGPL - original licence link has changed is not relivant.
1026  *
1027  * Fork - LGPL
1028  * <script type="text/javascript">
1029  */
1030  /**
1031  * @class Array
1032  */
1033 Roo.applyIf(Array.prototype, {
1034     /**
1035      * Checks whether or not the specified object exists in the array.
1036      * @param {Object} o The object to check for
1037      * @return {Number} The index of o in the array (or -1 if it is not found)
1038      */
1039     indexOf : function(o){
1040        for (var i = 0, len = this.length; i < len; i++){
1041               if(this[i] == o) return i;
1042        }
1043            return -1;
1044     },
1045
1046     /**
1047      * Removes the specified object from the array.  If the object is not found nothing happens.
1048      * @param {Object} o The object to remove
1049      */
1050     remove : function(o){
1051        var index = this.indexOf(o);
1052        if(index != -1){
1053            this.splice(index, 1);
1054        }
1055     },
1056     /**
1057      * Map (JS 1.6 compatibility)
1058      * @param {Function} function  to call
1059      */
1060     map : function(fun )
1061     {
1062         var len = this.length >>> 0;
1063         if (typeof fun != "function")
1064             throw new TypeError();
1065
1066         var res = new Array(len);
1067         var thisp = arguments[1];
1068         for (var i = 0; i < len; i++)
1069         {
1070             if (i in this)
1071                 res[i] = fun.call(thisp, this[i], i, this);
1072         }
1073
1074         return res;
1075     }
1076     
1077 });
1078
1079
1080  /*
1081  * Based on:
1082  * Ext JS Library 1.1.1
1083  * Copyright(c) 2006-2007, Ext JS, LLC.
1084  *
1085  * Originally Released Under LGPL - original licence link has changed is not relivant.
1086  *
1087  * Fork - LGPL
1088  * <script type="text/javascript">
1089  */
1090
1091 /**
1092  * @class Date
1093  *
1094  * The date parsing and format syntax is a subset of
1095  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1096  * supported will provide results equivalent to their PHP versions.
1097  *
1098  * Following is the list of all currently supported formats:
1099  *<pre>
1100 Sample date:
1101 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1102
1103 Format  Output      Description
1104 ------  ----------  --------------------------------------------------------------
1105   d      10         Day of the month, 2 digits with leading zeros
1106   D      Wed        A textual representation of a day, three letters
1107   j      10         Day of the month without leading zeros
1108   l      Wednesday  A full textual representation of the day of the week
1109   S      th         English ordinal day of month suffix, 2 chars (use with j)
1110   w      3          Numeric representation of the day of the week
1111   z      9          The julian date, or day of the year (0-365)
1112   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1113   F      January    A full textual representation of the month
1114   m      01         Numeric representation of a month, with leading zeros
1115   M      Jan        Month name abbreviation, three letters
1116   n      1          Numeric representation of a month, without leading zeros
1117   t      31         Number of days in the given month
1118   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1119   Y      2007       A full numeric representation of a year, 4 digits
1120   y      07         A two digit representation of a year
1121   a      pm         Lowercase Ante meridiem and Post meridiem
1122   A      PM         Uppercase Ante meridiem and Post meridiem
1123   g      3          12-hour format of an hour without leading zeros
1124   G      15         24-hour format of an hour without leading zeros
1125   h      03         12-hour format of an hour with leading zeros
1126   H      15         24-hour format of an hour with leading zeros
1127   i      05         Minutes with leading zeros
1128   s      01         Seconds, with leading zeros
1129   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1130   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1131   T      CST        Timezone setting of the machine running the code
1132   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1133 </pre>
1134  *
1135  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1136  * <pre><code>
1137 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1138 document.write(dt.format('Y-m-d'));                         //2007-01-10
1139 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1140 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
1141  </code></pre>
1142  *
1143  * Here are some standard date/time patterns that you might find helpful.  They
1144  * are not part of the source of Date.js, but to use them you can simply copy this
1145  * block of code into any script that is included after Date.js and they will also become
1146  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1147  * <pre><code>
1148 Date.patterns = {
1149     ISO8601Long:"Y-m-d H:i:s",
1150     ISO8601Short:"Y-m-d",
1151     ShortDate: "n/j/Y",
1152     LongDate: "l, F d, Y",
1153     FullDateTime: "l, F d, Y g:i:s A",
1154     MonthDay: "F d",
1155     ShortTime: "g:i A",
1156     LongTime: "g:i:s A",
1157     SortableDateTime: "Y-m-d\\TH:i:s",
1158     UniversalSortableDateTime: "Y-m-d H:i:sO",
1159     YearMonth: "F, Y"
1160 };
1161 </code></pre>
1162  *
1163  * Example usage:
1164  * <pre><code>
1165 var dt = new Date();
1166 document.write(dt.format(Date.patterns.ShortDate));
1167  </code></pre>
1168  */
1169
1170 /*
1171  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1172  * They generate precompiled functions from date formats instead of parsing and
1173  * processing the pattern every time you format a date.  These functions are available
1174  * on every Date object (any javascript function).
1175  *
1176  * The original article and download are here:
1177  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1178  *
1179  */
1180  
1181  
1182  // was in core
1183 /**
1184  Returns the number of milliseconds between this date and date
1185  @param {Date} date (optional) Defaults to now
1186  @return {Number} The diff in milliseconds
1187  @member Date getElapsed
1188  */
1189 Date.prototype.getElapsed = function(date) {
1190         return Math.abs((date || new Date()).getTime()-this.getTime());
1191 };
1192 // was in date file..
1193
1194
1195 // private
1196 Date.parseFunctions = {count:0};
1197 // private
1198 Date.parseRegexes = [];
1199 // private
1200 Date.formatFunctions = {count:0};
1201
1202 // private
1203 Date.prototype.dateFormat = function(format) {
1204     if (Date.formatFunctions[format] == null) {
1205         Date.createNewFormat(format);
1206     }
1207     var func = Date.formatFunctions[format];
1208     return this[func]();
1209 };
1210
1211
1212 /**
1213  * Formats a date given the supplied format string
1214  * @param {String} format The format string
1215  * @return {String} The formatted date
1216  * @method
1217  */
1218 Date.prototype.format = Date.prototype.dateFormat;
1219
1220 // private
1221 Date.createNewFormat = function(format) {
1222     var funcName = "format" + Date.formatFunctions.count++;
1223     Date.formatFunctions[format] = funcName;
1224     var code = "Date.prototype." + funcName + " = function(){return ";
1225     var special = false;
1226     var ch = '';
1227     for (var i = 0; i < format.length; ++i) {
1228         ch = format.charAt(i);
1229         if (!special && ch == "\\") {
1230             special = true;
1231         }
1232         else if (special) {
1233             special = false;
1234             code += "'" + String.escape(ch) + "' + ";
1235         }
1236         else {
1237             code += Date.getFormatCode(ch);
1238         }
1239     }
1240     /** eval:var:zzzzzzzzzzzzz */
1241     eval(code.substring(0, code.length - 3) + ";}");
1242 };
1243
1244 // private
1245 Date.getFormatCode = function(character) {
1246     switch (character) {
1247     case "d":
1248         return "String.leftPad(this.getDate(), 2, '0') + ";
1249     case "D":
1250         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1251     case "j":
1252         return "this.getDate() + ";
1253     case "l":
1254         return "Date.dayNames[this.getDay()] + ";
1255     case "S":
1256         return "this.getSuffix() + ";
1257     case "w":
1258         return "this.getDay() + ";
1259     case "z":
1260         return "this.getDayOfYear() + ";
1261     case "W":
1262         return "this.getWeekOfYear() + ";
1263     case "F":
1264         return "Date.monthNames[this.getMonth()] + ";
1265     case "m":
1266         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1267     case "M":
1268         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1269     case "n":
1270         return "(this.getMonth() + 1) + ";
1271     case "t":
1272         return "this.getDaysInMonth() + ";
1273     case "L":
1274         return "(this.isLeapYear() ? 1 : 0) + ";
1275     case "Y":
1276         return "this.getFullYear() + ";
1277     case "y":
1278         return "('' + this.getFullYear()).substring(2, 4) + ";
1279     case "a":
1280         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1281     case "A":
1282         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1283     case "g":
1284         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1285     case "G":
1286         return "this.getHours() + ";
1287     case "h":
1288         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1289     case "H":
1290         return "String.leftPad(this.getHours(), 2, '0') + ";
1291     case "i":
1292         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1293     case "s":
1294         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1295     case "O":
1296         return "this.getGMTOffset() + ";
1297     case "P":
1298         return "this.getGMTColonOffset() + ";
1299     case "T":
1300         return "this.getTimezone() + ";
1301     case "Z":
1302         return "(this.getTimezoneOffset() * -60) + ";
1303     default:
1304         return "'" + String.escape(character) + "' + ";
1305     }
1306 };
1307
1308 /**
1309  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1310  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1311  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1312  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1313  * string or the parse operation will fail.
1314  * Example Usage:
1315 <pre><code>
1316 //dt = Fri May 25 2007 (current date)
1317 var dt = new Date();
1318
1319 //dt = Thu May 25 2006 (today's month/day in 2006)
1320 dt = Date.parseDate("2006", "Y");
1321
1322 //dt = Sun Jan 15 2006 (all date parts specified)
1323 dt = Date.parseDate("2006-1-15", "Y-m-d");
1324
1325 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1326 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1327 </code></pre>
1328  * @param {String} input The unparsed date as a string
1329  * @param {String} format The format the date is in
1330  * @return {Date} The parsed date
1331  * @static
1332  */
1333 Date.parseDate = function(input, format) {
1334     if (Date.parseFunctions[format] == null) {
1335         Date.createParser(format);
1336     }
1337     var func = Date.parseFunctions[format];
1338     return Date[func](input);
1339 };
1340 /**
1341  * @private
1342  */
1343 Date.createParser = function(format) {
1344     var funcName = "parse" + Date.parseFunctions.count++;
1345     var regexNum = Date.parseRegexes.length;
1346     var currentGroup = 1;
1347     Date.parseFunctions[format] = funcName;
1348
1349     var code = "Date." + funcName + " = function(input){\n"
1350         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1351         + "var d = new Date();\n"
1352         + "y = d.getFullYear();\n"
1353         + "m = d.getMonth();\n"
1354         + "d = d.getDate();\n"
1355         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1356         + "if (results && results.length > 0) {";
1357     var regex = "";
1358
1359     var special = false;
1360     var ch = '';
1361     for (var i = 0; i < format.length; ++i) {
1362         ch = format.charAt(i);
1363         if (!special && ch == "\\") {
1364             special = true;
1365         }
1366         else if (special) {
1367             special = false;
1368             regex += String.escape(ch);
1369         }
1370         else {
1371             var obj = Date.formatCodeToRegex(ch, currentGroup);
1372             currentGroup += obj.g;
1373             regex += obj.s;
1374             if (obj.g && obj.c) {
1375                 code += obj.c;
1376             }
1377         }
1378     }
1379
1380     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1381         + "{v = new Date(y, m, d, h, i, s);}\n"
1382         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1383         + "{v = new Date(y, m, d, h, i);}\n"
1384         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1385         + "{v = new Date(y, m, d, h);}\n"
1386         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1387         + "{v = new Date(y, m, d);}\n"
1388         + "else if (y >= 0 && m >= 0)\n"
1389         + "{v = new Date(y, m);}\n"
1390         + "else if (y >= 0)\n"
1391         + "{v = new Date(y);}\n"
1392         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1393         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1394         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1395         + ";}";
1396
1397     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1398     /** eval:var:zzzzzzzzzzzzz */
1399     eval(code);
1400 };
1401
1402 // private
1403 Date.formatCodeToRegex = function(character, currentGroup) {
1404     switch (character) {
1405     case "D":
1406         return {g:0,
1407         c:null,
1408         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1409     case "j":
1410         return {g:1,
1411             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1412             s:"(\\d{1,2})"}; // day of month without leading zeroes
1413     case "d":
1414         return {g:1,
1415             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1416             s:"(\\d{2})"}; // day of month with leading zeroes
1417     case "l":
1418         return {g:0,
1419             c:null,
1420             s:"(?:" + Date.dayNames.join("|") + ")"};
1421     case "S":
1422         return {g:0,
1423             c:null,
1424             s:"(?:st|nd|rd|th)"};
1425     case "w":
1426         return {g:0,
1427             c:null,
1428             s:"\\d"};
1429     case "z":
1430         return {g:0,
1431             c:null,
1432             s:"(?:\\d{1,3})"};
1433     case "W":
1434         return {g:0,
1435             c:null,
1436             s:"(?:\\d{2})"};
1437     case "F":
1438         return {g:1,
1439             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1440             s:"(" + Date.monthNames.join("|") + ")"};
1441     case "M":
1442         return {g:1,
1443             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1444             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1445     case "n":
1446         return {g:1,
1447             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1448             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1449     case "m":
1450         return {g:1,
1451             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1452             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1453     case "t":
1454         return {g:0,
1455             c:null,
1456             s:"\\d{1,2}"};
1457     case "L":
1458         return {g:0,
1459             c:null,
1460             s:"(?:1|0)"};
1461     case "Y":
1462         return {g:1,
1463             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1464             s:"(\\d{4})"};
1465     case "y":
1466         return {g:1,
1467             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1468                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1469             s:"(\\d{1,2})"};
1470     case "a":
1471         return {g:1,
1472             c:"if (results[" + currentGroup + "] == 'am') {\n"
1473                 + "if (h == 12) { h = 0; }\n"
1474                 + "} else { if (h < 12) { h += 12; }}",
1475             s:"(am|pm)"};
1476     case "A":
1477         return {g:1,
1478             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1479                 + "if (h == 12) { h = 0; }\n"
1480                 + "} else { if (h < 12) { h += 12; }}",
1481             s:"(AM|PM)"};
1482     case "g":
1483     case "G":
1484         return {g:1,
1485             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1486             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1487     case "h":
1488     case "H":
1489         return {g:1,
1490             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1491             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1492     case "i":
1493         return {g:1,
1494             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1495             s:"(\\d{2})"};
1496     case "s":
1497         return {g:1,
1498             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1499             s:"(\\d{2})"};
1500     case "O":
1501         return {g:1,
1502             c:[
1503                 "o = results[", currentGroup, "];\n",
1504                 "var sn = o.substring(0,1);\n", // get + / - sign
1505                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1506                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1507                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1508                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1509             ].join(""),
1510             s:"([+\-]\\d{2,4})"};
1511     
1512     
1513     case "P":
1514         return {g:1,
1515                 c:[
1516                    "o = results[", currentGroup, "];\n",
1517                    "var sn = o.substring(0,1);\n",
1518                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1519                    "var mn = o.substring(4,6) % 60;\n",
1520                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1521                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1522             ].join(""),
1523             s:"([+\-]\\d{4})"};
1524     case "T":
1525         return {g:0,
1526             c:null,
1527             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1528     case "Z":
1529         return {g:1,
1530             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1531                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1532             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1533     default:
1534         return {g:0,
1535             c:null,
1536             s:String.escape(character)};
1537     }
1538 };
1539
1540 /**
1541  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1542  * @return {String} The abbreviated timezone name (e.g. 'CST')
1543  */
1544 Date.prototype.getTimezone = function() {
1545     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1546 };
1547
1548 /**
1549  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1550  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1551  */
1552 Date.prototype.getGMTOffset = function() {
1553     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1554         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1555         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1556 };
1557
1558 /**
1559  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1560  * @return {String} 2-characters representing hours and 2-characters representing minutes
1561  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1562  */
1563 Date.prototype.getGMTColonOffset = function() {
1564         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1565                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1566                 + ":"
1567                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1568 }
1569
1570 /**
1571  * Get the numeric day number of the year, adjusted for leap year.
1572  * @return {Number} 0 through 364 (365 in leap years)
1573  */
1574 Date.prototype.getDayOfYear = function() {
1575     var num = 0;
1576     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1577     for (var i = 0; i < this.getMonth(); ++i) {
1578         num += Date.daysInMonth[i];
1579     }
1580     return num + this.getDate() - 1;
1581 };
1582
1583 /**
1584  * Get the string representation of the numeric week number of the year
1585  * (equivalent to the format specifier 'W').
1586  * @return {String} '00' through '52'
1587  */
1588 Date.prototype.getWeekOfYear = function() {
1589     // Skip to Thursday of this week
1590     var now = this.getDayOfYear() + (4 - this.getDay());
1591     // Find the first Thursday of the year
1592     var jan1 = new Date(this.getFullYear(), 0, 1);
1593     var then = (7 - jan1.getDay() + 4);
1594     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1595 };
1596
1597 /**
1598  * Whether or not the current date is in a leap year.
1599  * @return {Boolean} True if the current date is in a leap year, else false
1600  */
1601 Date.prototype.isLeapYear = function() {
1602     var year = this.getFullYear();
1603     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1604 };
1605
1606 /**
1607  * Get the first day of the current month, adjusted for leap year.  The returned value
1608  * is the numeric day index within the week (0-6) which can be used in conjunction with
1609  * the {@link #monthNames} array to retrieve the textual day name.
1610  * Example:
1611  *<pre><code>
1612 var dt = new Date('1/10/2007');
1613 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1614 </code></pre>
1615  * @return {Number} The day number (0-6)
1616  */
1617 Date.prototype.getFirstDayOfMonth = function() {
1618     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1619     return (day < 0) ? (day + 7) : day;
1620 };
1621
1622 /**
1623  * Get the last day of the current month, adjusted for leap year.  The returned value
1624  * is the numeric day index within the week (0-6) which can be used in conjunction with
1625  * the {@link #monthNames} array to retrieve the textual day name.
1626  * Example:
1627  *<pre><code>
1628 var dt = new Date('1/10/2007');
1629 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1630 </code></pre>
1631  * @return {Number} The day number (0-6)
1632  */
1633 Date.prototype.getLastDayOfMonth = function() {
1634     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1635     return (day < 0) ? (day + 7) : day;
1636 };
1637
1638
1639 /**
1640  * Get the first date of this date's month
1641  * @return {Date}
1642  */
1643 Date.prototype.getFirstDateOfMonth = function() {
1644     return new Date(this.getFullYear(), this.getMonth(), 1);
1645 };
1646
1647 /**
1648  * Get the last date of this date's month
1649  * @return {Date}
1650  */
1651 Date.prototype.getLastDateOfMonth = function() {
1652     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1653 };
1654 /**
1655  * Get the number of days in the current month, adjusted for leap year.
1656  * @return {Number} The number of days in the month
1657  */
1658 Date.prototype.getDaysInMonth = function() {
1659     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1660     return Date.daysInMonth[this.getMonth()];
1661 };
1662
1663 /**
1664  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1665  * @return {String} 'st, 'nd', 'rd' or 'th'
1666  */
1667 Date.prototype.getSuffix = function() {
1668     switch (this.getDate()) {
1669         case 1:
1670         case 21:
1671         case 31:
1672             return "st";
1673         case 2:
1674         case 22:
1675             return "nd";
1676         case 3:
1677         case 23:
1678             return "rd";
1679         default:
1680             return "th";
1681     }
1682 };
1683
1684 // private
1685 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1686
1687 /**
1688  * An array of textual month names.
1689  * Override these values for international dates, for example...
1690  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1691  * @type Array
1692  * @static
1693  */
1694 Date.monthNames =
1695    ["January",
1696     "February",
1697     "March",
1698     "April",
1699     "May",
1700     "June",
1701     "July",
1702     "August",
1703     "September",
1704     "October",
1705     "November",
1706     "December"];
1707
1708 /**
1709  * An array of textual day names.
1710  * Override these values for international dates, for example...
1711  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1712  * @type Array
1713  * @static
1714  */
1715 Date.dayNames =
1716    ["Sunday",
1717     "Monday",
1718     "Tuesday",
1719     "Wednesday",
1720     "Thursday",
1721     "Friday",
1722     "Saturday"];
1723
1724 // private
1725 Date.y2kYear = 50;
1726 // private
1727 Date.monthNumbers = {
1728     Jan:0,
1729     Feb:1,
1730     Mar:2,
1731     Apr:3,
1732     May:4,
1733     Jun:5,
1734     Jul:6,
1735     Aug:7,
1736     Sep:8,
1737     Oct:9,
1738     Nov:10,
1739     Dec:11};
1740
1741 /**
1742  * Creates and returns a new Date instance with the exact same date value as the called instance.
1743  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1744  * variable will also be changed.  When the intention is to create a new variable that will not
1745  * modify the original instance, you should create a clone.
1746  *
1747  * Example of correctly cloning a date:
1748  * <pre><code>
1749 //wrong way:
1750 var orig = new Date('10/1/2006');
1751 var copy = orig;
1752 copy.setDate(5);
1753 document.write(orig);  //returns 'Thu Oct 05 2006'!
1754
1755 //correct way:
1756 var orig = new Date('10/1/2006');
1757 var copy = orig.clone();
1758 copy.setDate(5);
1759 document.write(orig);  //returns 'Thu Oct 01 2006'
1760 </code></pre>
1761  * @return {Date} The new Date instance
1762  */
1763 Date.prototype.clone = function() {
1764         return new Date(this.getTime());
1765 };
1766
1767 /**
1768  * Clears any time information from this date
1769  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1770  @return {Date} this or the clone
1771  */
1772 Date.prototype.clearTime = function(clone){
1773     if(clone){
1774         return this.clone().clearTime();
1775     }
1776     this.setHours(0);
1777     this.setMinutes(0);
1778     this.setSeconds(0);
1779     this.setMilliseconds(0);
1780     return this;
1781 };
1782
1783 // private
1784 // safari setMonth is broken
1785 if(Roo.isSafari){
1786     Date.brokenSetMonth = Date.prototype.setMonth;
1787         Date.prototype.setMonth = function(num){
1788                 if(num <= -1){
1789                         var n = Math.ceil(-num);
1790                         var back_year = Math.ceil(n/12);
1791                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1792                         this.setFullYear(this.getFullYear() - back_year);
1793                         return Date.brokenSetMonth.call(this, month);
1794                 } else {
1795                         return Date.brokenSetMonth.apply(this, arguments);
1796                 }
1797         };
1798 }
1799
1800 /** Date interval constant 
1801 * @static 
1802 * @type String */
1803 Date.MILLI = "ms";
1804 /** Date interval constant 
1805 * @static 
1806 * @type String */
1807 Date.SECOND = "s";
1808 /** Date interval constant 
1809 * @static 
1810 * @type String */
1811 Date.MINUTE = "mi";
1812 /** Date interval constant 
1813 * @static 
1814 * @type String */
1815 Date.HOUR = "h";
1816 /** Date interval constant 
1817 * @static 
1818 * @type String */
1819 Date.DAY = "d";
1820 /** Date interval constant 
1821 * @static 
1822 * @type String */
1823 Date.MONTH = "mo";
1824 /** Date interval constant 
1825 * @static 
1826 * @type String */
1827 Date.YEAR = "y";
1828
1829 /**
1830  * Provides a convenient method of performing basic date arithmetic.  This method
1831  * does not modify the Date instance being called - it creates and returns
1832  * a new Date instance containing the resulting date value.
1833  *
1834  * Examples:
1835  * <pre><code>
1836 //Basic usage:
1837 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1838 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1839
1840 //Negative values will subtract correctly:
1841 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1842 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1843
1844 //You can even chain several calls together in one line!
1845 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1846 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1847  </code></pre>
1848  *
1849  * @param {String} interval   A valid date interval enum value
1850  * @param {Number} value      The amount to add to the current date
1851  * @return {Date} The new Date instance
1852  */
1853 Date.prototype.add = function(interval, value){
1854   var d = this.clone();
1855   if (!interval || value === 0) return d;
1856   switch(interval.toLowerCase()){
1857     case Date.MILLI:
1858       d.setMilliseconds(this.getMilliseconds() + value);
1859       break;
1860     case Date.SECOND:
1861       d.setSeconds(this.getSeconds() + value);
1862       break;
1863     case Date.MINUTE:
1864       d.setMinutes(this.getMinutes() + value);
1865       break;
1866     case Date.HOUR:
1867       d.setHours(this.getHours() + value);
1868       break;
1869     case Date.DAY:
1870       d.setDate(this.getDate() + value);
1871       break;
1872     case Date.MONTH:
1873       var day = this.getDate();
1874       if(day > 28){
1875           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1876       }
1877       d.setDate(day);
1878       d.setMonth(this.getMonth() + value);
1879       break;
1880     case Date.YEAR:
1881       d.setFullYear(this.getFullYear() + value);
1882       break;
1883   }
1884   return d;
1885 };
1886 /*
1887  * Based on:
1888  * Ext JS Library 1.1.1
1889  * Copyright(c) 2006-2007, Ext JS, LLC.
1890  *
1891  * Originally Released Under LGPL - original licence link has changed is not relivant.
1892  *
1893  * Fork - LGPL
1894  * <script type="text/javascript">
1895  */
1896
1897 /**
1898  * @class Roo.lib.Dom
1899  * @static
1900  * 
1901  * Dom utils (from YIU afaik)
1902  * 
1903  **/
1904 Roo.lib.Dom = {
1905     /**
1906      * Get the view width
1907      * @param {Boolean} full True will get the full document, otherwise it's the view width
1908      * @return {Number} The width
1909      */
1910      
1911     getViewWidth : function(full) {
1912         return full ? this.getDocumentWidth() : this.getViewportWidth();
1913     },
1914     /**
1915      * Get the view height
1916      * @param {Boolean} full True will get the full document, otherwise it's the view height
1917      * @return {Number} The height
1918      */
1919     getViewHeight : function(full) {
1920         return full ? this.getDocumentHeight() : this.getViewportHeight();
1921     },
1922
1923     getDocumentHeight: function() {
1924         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1925         return Math.max(scrollHeight, this.getViewportHeight());
1926     },
1927
1928     getDocumentWidth: function() {
1929         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1930         return Math.max(scrollWidth, this.getViewportWidth());
1931     },
1932
1933     getViewportHeight: function() {
1934         var height = self.innerHeight;
1935         var mode = document.compatMode;
1936
1937         if ((mode || Roo.isIE) && !Roo.isOpera) {
1938             height = (mode == "CSS1Compat") ?
1939                      document.documentElement.clientHeight :
1940                      document.body.clientHeight;
1941         }
1942
1943         return height;
1944     },
1945
1946     getViewportWidth: function() {
1947         var width = self.innerWidth;
1948         var mode = document.compatMode;
1949
1950         if (mode || Roo.isIE) {
1951             width = (mode == "CSS1Compat") ?
1952                     document.documentElement.clientWidth :
1953                     document.body.clientWidth;
1954         }
1955         return width;
1956     },
1957
1958     isAncestor : function(p, c) {
1959         p = Roo.getDom(p);
1960         c = Roo.getDom(c);
1961         if (!p || !c) {
1962             return false;
1963         }
1964
1965         if (p.contains && !Roo.isSafari) {
1966             return p.contains(c);
1967         } else if (p.compareDocumentPosition) {
1968             return !!(p.compareDocumentPosition(c) & 16);
1969         } else {
1970             var parent = c.parentNode;
1971             while (parent) {
1972                 if (parent == p) {
1973                     return true;
1974                 }
1975                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1976                     return false;
1977                 }
1978                 parent = parent.parentNode;
1979             }
1980             return false;
1981         }
1982     },
1983
1984     getRegion : function(el) {
1985         return Roo.lib.Region.getRegion(el);
1986     },
1987
1988     getY : function(el) {
1989         return this.getXY(el)[1];
1990     },
1991
1992     getX : function(el) {
1993         return this.getXY(el)[0];
1994     },
1995
1996     getXY : function(el) {
1997         var p, pe, b, scroll, bd = document.body;
1998         el = Roo.getDom(el);
1999         var fly = Roo.lib.AnimBase.fly;
2000         if (el.getBoundingClientRect) {
2001             b = el.getBoundingClientRect();
2002             scroll = fly(document).getScroll();
2003             return [b.left + scroll.left, b.top + scroll.top];
2004         }
2005         var x = 0, y = 0;
2006
2007         p = el;
2008
2009         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2010
2011         while (p) {
2012
2013             x += p.offsetLeft;
2014             y += p.offsetTop;
2015
2016             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2017                 hasAbsolute = true;
2018             }
2019
2020             if (Roo.isGecko) {
2021                 pe = fly(p);
2022
2023                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2024                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2025
2026
2027                 x += bl;
2028                 y += bt;
2029
2030
2031                 if (p != el && pe.getStyle('overflow') != 'visible') {
2032                     x += bl;
2033                     y += bt;
2034                 }
2035             }
2036             p = p.offsetParent;
2037         }
2038
2039         if (Roo.isSafari && hasAbsolute) {
2040             x -= bd.offsetLeft;
2041             y -= bd.offsetTop;
2042         }
2043
2044         if (Roo.isGecko && !hasAbsolute) {
2045             var dbd = fly(bd);
2046             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2047             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2048         }
2049
2050         p = el.parentNode;
2051         while (p && p != bd) {
2052             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2053                 x -= p.scrollLeft;
2054                 y -= p.scrollTop;
2055             }
2056             p = p.parentNode;
2057         }
2058         return [x, y];
2059     },
2060  
2061   
2062
2063
2064     setXY : function(el, xy) {
2065         el = Roo.fly(el, '_setXY');
2066         el.position();
2067         var pts = el.translatePoints(xy);
2068         if (xy[0] !== false) {
2069             el.dom.style.left = pts.left + "px";
2070         }
2071         if (xy[1] !== false) {
2072             el.dom.style.top = pts.top + "px";
2073         }
2074     },
2075
2076     setX : function(el, x) {
2077         this.setXY(el, [x, false]);
2078     },
2079
2080     setY : function(el, y) {
2081         this.setXY(el, [false, y]);
2082     }
2083 };
2084 /*
2085  * Portions of this file are based on pieces of Yahoo User Interface Library
2086  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2087  * YUI licensed under the BSD License:
2088  * http://developer.yahoo.net/yui/license.txt
2089  * <script type="text/javascript">
2090  *
2091  */
2092
2093 Roo.lib.Event = function() {
2094     var loadComplete = false;
2095     var listeners = [];
2096     var unloadListeners = [];
2097     var retryCount = 0;
2098     var onAvailStack = [];
2099     var counter = 0;
2100     var lastError = null;
2101
2102     return {
2103         POLL_RETRYS: 200,
2104         POLL_INTERVAL: 20,
2105         EL: 0,
2106         TYPE: 1,
2107         FN: 2,
2108         WFN: 3,
2109         OBJ: 3,
2110         ADJ_SCOPE: 4,
2111         _interval: null,
2112
2113         startInterval: function() {
2114             if (!this._interval) {
2115                 var self = this;
2116                 var callback = function() {
2117                     self._tryPreloadAttach();
2118                 };
2119                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2120
2121             }
2122         },
2123
2124         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2125             onAvailStack.push({ id:         p_id,
2126                 fn:         p_fn,
2127                 obj:        p_obj,
2128                 override:   p_override,
2129                 checkReady: false    });
2130
2131             retryCount = this.POLL_RETRYS;
2132             this.startInterval();
2133         },
2134
2135
2136         addListener: function(el, eventName, fn) {
2137             el = Roo.getDom(el);
2138             if (!el || !fn) {
2139                 return false;
2140             }
2141
2142             if ("unload" == eventName) {
2143                 unloadListeners[unloadListeners.length] =
2144                 [el, eventName, fn];
2145                 return true;
2146             }
2147
2148             var wrappedFn = function(e) {
2149                 return fn(Roo.lib.Event.getEvent(e));
2150             };
2151
2152             var li = [el, eventName, fn, wrappedFn];
2153
2154             var index = listeners.length;
2155             listeners[index] = li;
2156
2157             this.doAdd(el, eventName, wrappedFn, false);
2158             return true;
2159
2160         },
2161
2162
2163         removeListener: function(el, eventName, fn) {
2164             var i, len;
2165
2166             el = Roo.getDom(el);
2167
2168             if(!fn) {
2169                 return this.purgeElement(el, false, eventName);
2170             }
2171
2172
2173             if ("unload" == eventName) {
2174
2175                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2176                     var li = unloadListeners[i];
2177                     if (li &&
2178                         li[0] == el &&
2179                         li[1] == eventName &&
2180                         li[2] == fn) {
2181                         unloadListeners.splice(i, 1);
2182                         return true;
2183                     }
2184                 }
2185
2186                 return false;
2187             }
2188
2189             var cacheItem = null;
2190
2191
2192             var index = arguments[3];
2193
2194             if ("undefined" == typeof index) {
2195                 index = this._getCacheIndex(el, eventName, fn);
2196             }
2197
2198             if (index >= 0) {
2199                 cacheItem = listeners[index];
2200             }
2201
2202             if (!el || !cacheItem) {
2203                 return false;
2204             }
2205
2206             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2207
2208             delete listeners[index][this.WFN];
2209             delete listeners[index][this.FN];
2210             listeners.splice(index, 1);
2211
2212             return true;
2213
2214         },
2215
2216
2217         getTarget: function(ev, resolveTextNode) {
2218             ev = ev.browserEvent || ev;
2219             var t = ev.target || ev.srcElement;
2220             return this.resolveTextNode(t);
2221         },
2222
2223
2224         resolveTextNode: function(node) {
2225             if (Roo.isSafari && node && 3 == node.nodeType) {
2226                 return node.parentNode;
2227             } else {
2228                 return node;
2229             }
2230         },
2231
2232
2233         getPageX: function(ev) {
2234             ev = ev.browserEvent || ev;
2235             var x = ev.pageX;
2236             if (!x && 0 !== x) {
2237                 x = ev.clientX || 0;
2238
2239                 if (Roo.isIE) {
2240                     x += this.getScroll()[1];
2241                 }
2242             }
2243
2244             return x;
2245         },
2246
2247
2248         getPageY: function(ev) {
2249             ev = ev.browserEvent || ev;
2250             var y = ev.pageY;
2251             if (!y && 0 !== y) {
2252                 y = ev.clientY || 0;
2253
2254                 if (Roo.isIE) {
2255                     y += this.getScroll()[0];
2256                 }
2257             }
2258
2259
2260             return y;
2261         },
2262
2263
2264         getXY: function(ev) {
2265             ev = ev.browserEvent || ev;
2266             return [this.getPageX(ev), this.getPageY(ev)];
2267         },
2268
2269
2270         getRelatedTarget: function(ev) {
2271             ev = ev.browserEvent || ev;
2272             var t = ev.relatedTarget;
2273             if (!t) {
2274                 if (ev.type == "mouseout") {
2275                     t = ev.toElement;
2276                 } else if (ev.type == "mouseover") {
2277                     t = ev.fromElement;
2278                 }
2279             }
2280
2281             return this.resolveTextNode(t);
2282         },
2283
2284
2285         getTime: function(ev) {
2286             ev = ev.browserEvent || ev;
2287             if (!ev.time) {
2288                 var t = new Date().getTime();
2289                 try {
2290                     ev.time = t;
2291                 } catch(ex) {
2292                     this.lastError = ex;
2293                     return t;
2294                 }
2295             }
2296
2297             return ev.time;
2298         },
2299
2300
2301         stopEvent: function(ev) {
2302             this.stopPropagation(ev);
2303             this.preventDefault(ev);
2304         },
2305
2306
2307         stopPropagation: function(ev) {
2308             ev = ev.browserEvent || ev;
2309             if (ev.stopPropagation) {
2310                 ev.stopPropagation();
2311             } else {
2312                 ev.cancelBubble = true;
2313             }
2314         },
2315
2316
2317         preventDefault: function(ev) {
2318             ev = ev.browserEvent || ev;
2319             if(ev.preventDefault) {
2320                 ev.preventDefault();
2321             } else {
2322                 ev.returnValue = false;
2323             }
2324         },
2325
2326
2327         getEvent: function(e) {
2328             var ev = e || window.event;
2329             if (!ev) {
2330                 var c = this.getEvent.caller;
2331                 while (c) {
2332                     ev = c.arguments[0];
2333                     if (ev && Event == ev.constructor) {
2334                         break;
2335                     }
2336                     c = c.caller;
2337                 }
2338             }
2339             return ev;
2340         },
2341
2342
2343         getCharCode: function(ev) {
2344             ev = ev.browserEvent || ev;
2345             return ev.charCode || ev.keyCode || 0;
2346         },
2347
2348
2349         _getCacheIndex: function(el, eventName, fn) {
2350             for (var i = 0,len = listeners.length; i < len; ++i) {
2351                 var li = listeners[i];
2352                 if (li &&
2353                     li[this.FN] == fn &&
2354                     li[this.EL] == el &&
2355                     li[this.TYPE] == eventName) {
2356                     return i;
2357                 }
2358             }
2359
2360             return -1;
2361         },
2362
2363
2364         elCache: {},
2365
2366
2367         getEl: function(id) {
2368             return document.getElementById(id);
2369         },
2370
2371
2372         clearCache: function() {
2373         },
2374
2375
2376         _load: function(e) {
2377             loadComplete = true;
2378             var EU = Roo.lib.Event;
2379
2380
2381             if (Roo.isIE) {
2382                 EU.doRemove(window, "load", EU._load);
2383             }
2384         },
2385
2386
2387         _tryPreloadAttach: function() {
2388
2389             if (this.locked) {
2390                 return false;
2391             }
2392
2393             this.locked = true;
2394
2395
2396             var tryAgain = !loadComplete;
2397             if (!tryAgain) {
2398                 tryAgain = (retryCount > 0);
2399             }
2400
2401
2402             var notAvail = [];
2403             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2404                 var item = onAvailStack[i];
2405                 if (item) {
2406                     var el = this.getEl(item.id);
2407
2408                     if (el) {
2409                         if (!item.checkReady ||
2410                             loadComplete ||
2411                             el.nextSibling ||
2412                             (document && document.body)) {
2413
2414                             var scope = el;
2415                             if (item.override) {
2416                                 if (item.override === true) {
2417                                     scope = item.obj;
2418                                 } else {
2419                                     scope = item.override;
2420                                 }
2421                             }
2422                             item.fn.call(scope, item.obj);
2423                             onAvailStack[i] = null;
2424                         }
2425                     } else {
2426                         notAvail.push(item);
2427                     }
2428                 }
2429             }
2430
2431             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2432
2433             if (tryAgain) {
2434
2435                 this.startInterval();
2436             } else {
2437                 clearInterval(this._interval);
2438                 this._interval = null;
2439             }
2440
2441             this.locked = false;
2442
2443             return true;
2444
2445         },
2446
2447
2448         purgeElement: function(el, recurse, eventName) {
2449             var elListeners = this.getListeners(el, eventName);
2450             if (elListeners) {
2451                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2452                     var l = elListeners[i];
2453                     this.removeListener(el, l.type, l.fn);
2454                 }
2455             }
2456
2457             if (recurse && el && el.childNodes) {
2458                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2459                     this.purgeElement(el.childNodes[i], recurse, eventName);
2460                 }
2461             }
2462         },
2463
2464
2465         getListeners: function(el, eventName) {
2466             var results = [], searchLists;
2467             if (!eventName) {
2468                 searchLists = [listeners, unloadListeners];
2469             } else if (eventName == "unload") {
2470                 searchLists = [unloadListeners];
2471             } else {
2472                 searchLists = [listeners];
2473             }
2474
2475             for (var j = 0; j < searchLists.length; ++j) {
2476                 var searchList = searchLists[j];
2477                 if (searchList && searchList.length > 0) {
2478                     for (var i = 0,len = searchList.length; i < len; ++i) {
2479                         var l = searchList[i];
2480                         if (l && l[this.EL] === el &&
2481                             (!eventName || eventName === l[this.TYPE])) {
2482                             results.push({
2483                                 type:   l[this.TYPE],
2484                                 fn:     l[this.FN],
2485                                 obj:    l[this.OBJ],
2486                                 adjust: l[this.ADJ_SCOPE],
2487                                 index:  i
2488                             });
2489                         }
2490                     }
2491                 }
2492             }
2493
2494             return (results.length) ? results : null;
2495         },
2496
2497
2498         _unload: function(e) {
2499
2500             var EU = Roo.lib.Event, i, j, l, len, index;
2501
2502             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2503                 l = unloadListeners[i];
2504                 if (l) {
2505                     var scope = window;
2506                     if (l[EU.ADJ_SCOPE]) {
2507                         if (l[EU.ADJ_SCOPE] === true) {
2508                             scope = l[EU.OBJ];
2509                         } else {
2510                             scope = l[EU.ADJ_SCOPE];
2511                         }
2512                     }
2513                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2514                     unloadListeners[i] = null;
2515                     l = null;
2516                     scope = null;
2517                 }
2518             }
2519
2520             unloadListeners = null;
2521
2522             if (listeners && listeners.length > 0) {
2523                 j = listeners.length;
2524                 while (j) {
2525                     index = j - 1;
2526                     l = listeners[index];
2527                     if (l) {
2528                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2529                                 l[EU.FN], index);
2530                     }
2531                     j = j - 1;
2532                 }
2533                 l = null;
2534
2535                 EU.clearCache();
2536             }
2537
2538             EU.doRemove(window, "unload", EU._unload);
2539
2540         },
2541
2542
2543         getScroll: function() {
2544             var dd = document.documentElement, db = document.body;
2545             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2546                 return [dd.scrollTop, dd.scrollLeft];
2547             } else if (db) {
2548                 return [db.scrollTop, db.scrollLeft];
2549             } else {
2550                 return [0, 0];
2551             }
2552         },
2553
2554
2555         doAdd: function () {
2556             if (window.addEventListener) {
2557                 return function(el, eventName, fn, capture) {
2558                     el.addEventListener(eventName, fn, (capture));
2559                 };
2560             } else if (window.attachEvent) {
2561                 return function(el, eventName, fn, capture) {
2562                     el.attachEvent("on" + eventName, fn);
2563                 };
2564             } else {
2565                 return function() {
2566                 };
2567             }
2568         }(),
2569
2570
2571         doRemove: function() {
2572             if (window.removeEventListener) {
2573                 return function (el, eventName, fn, capture) {
2574                     el.removeEventListener(eventName, fn, (capture));
2575                 };
2576             } else if (window.detachEvent) {
2577                 return function (el, eventName, fn) {
2578                     el.detachEvent("on" + eventName, fn);
2579                 };
2580             } else {
2581                 return function() {
2582                 };
2583             }
2584         }()
2585     };
2586     
2587 }();
2588 (function() {     
2589    
2590     var E = Roo.lib.Event;
2591     E.on = E.addListener;
2592     E.un = E.removeListener;
2593
2594     if (document && document.body) {
2595         E._load();
2596     } else {
2597         E.doAdd(window, "load", E._load);
2598     }
2599     E.doAdd(window, "unload", E._unload);
2600     E._tryPreloadAttach();
2601 })();
2602
2603 /*
2604  * Portions of this file are based on pieces of Yahoo User Interface Library
2605  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2606  * YUI licensed under the BSD License:
2607  * http://developer.yahoo.net/yui/license.txt
2608  * <script type="text/javascript">
2609  *
2610  */
2611
2612 (function() {
2613     /**
2614      * @class Roo.lib.Ajax
2615      *
2616      */
2617     Roo.lib.Ajax = {
2618         /**
2619          * @static 
2620          */
2621         request : function(method, uri, cb, data, options) {
2622             if(options){
2623                 var hs = options.headers;
2624                 if(hs){
2625                     for(var h in hs){
2626                         if(hs.hasOwnProperty(h)){
2627                             this.initHeader(h, hs[h], false);
2628                         }
2629                     }
2630                 }
2631                 if(options.xmlData){
2632                     this.initHeader('Content-Type', 'text/xml', false);
2633                     method = 'POST';
2634                     data = options.xmlData;
2635                 }
2636             }
2637
2638             return this.asyncRequest(method, uri, cb, data);
2639         },
2640
2641         serializeForm : function(form) {
2642             if(typeof form == 'string') {
2643                 form = (document.getElementById(form) || document.forms[form]);
2644             }
2645
2646             var el, name, val, disabled, data = '', hasSubmit = false;
2647             for (var i = 0; i < form.elements.length; i++) {
2648                 el = form.elements[i];
2649                 disabled = form.elements[i].disabled;
2650                 name = form.elements[i].name;
2651                 val = form.elements[i].value;
2652
2653                 if (!disabled && name){
2654                     switch (el.type)
2655                             {
2656                         case 'select-one':
2657                         case 'select-multiple':
2658                             for (var j = 0; j < el.options.length; j++) {
2659                                 if (el.options[j].selected) {
2660                                     if (Roo.isIE) {
2661                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2662                                     }
2663                                     else {
2664                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2665                                     }
2666                                 }
2667                             }
2668                             break;
2669                         case 'radio':
2670                         case 'checkbox':
2671                             if (el.checked) {
2672                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2673                             }
2674                             break;
2675                         case 'file':
2676
2677                         case undefined:
2678
2679                         case 'reset':
2680
2681                         case 'button':
2682
2683                             break;
2684                         case 'submit':
2685                             if(hasSubmit == false) {
2686                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2687                                 hasSubmit = true;
2688                             }
2689                             break;
2690                         default:
2691                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2692                             break;
2693                     }
2694                 }
2695             }
2696             data = data.substr(0, data.length - 1);
2697             return data;
2698         },
2699
2700         headers:{},
2701
2702         hasHeaders:false,
2703
2704         useDefaultHeader:true,
2705
2706         defaultPostHeader:'application/x-www-form-urlencoded',
2707
2708         useDefaultXhrHeader:true,
2709
2710         defaultXhrHeader:'XMLHttpRequest',
2711
2712         hasDefaultHeaders:true,
2713
2714         defaultHeaders:{},
2715
2716         poll:{},
2717
2718         timeout:{},
2719
2720         pollInterval:50,
2721
2722         transactionId:0,
2723
2724         setProgId:function(id)
2725         {
2726             this.activeX.unshift(id);
2727         },
2728
2729         setDefaultPostHeader:function(b)
2730         {
2731             this.useDefaultHeader = b;
2732         },
2733
2734         setDefaultXhrHeader:function(b)
2735         {
2736             this.useDefaultXhrHeader = b;
2737         },
2738
2739         setPollingInterval:function(i)
2740         {
2741             if (typeof i == 'number' && isFinite(i)) {
2742                 this.pollInterval = i;
2743             }
2744         },
2745
2746         createXhrObject:function(transactionId)
2747         {
2748             var obj,http;
2749             try
2750             {
2751
2752                 http = new XMLHttpRequest();
2753
2754                 obj = { conn:http, tId:transactionId };
2755             }
2756             catch(e)
2757             {
2758                 for (var i = 0; i < this.activeX.length; ++i) {
2759                     try
2760                     {
2761
2762                         http = new ActiveXObject(this.activeX[i]);
2763
2764                         obj = { conn:http, tId:transactionId };
2765                         break;
2766                     }
2767                     catch(e) {
2768                     }
2769                 }
2770             }
2771             finally
2772             {
2773                 return obj;
2774             }
2775         },
2776
2777         getConnectionObject:function()
2778         {
2779             var o;
2780             var tId = this.transactionId;
2781
2782             try
2783             {
2784                 o = this.createXhrObject(tId);
2785                 if (o) {
2786                     this.transactionId++;
2787                 }
2788             }
2789             catch(e) {
2790             }
2791             finally
2792             {
2793                 return o;
2794             }
2795         },
2796
2797         asyncRequest:function(method, uri, callback, postData)
2798         {
2799             var o = this.getConnectionObject();
2800
2801             if (!o) {
2802                 return null;
2803             }
2804             else {
2805                 o.conn.open(method, uri, true);
2806
2807                 if (this.useDefaultXhrHeader) {
2808                     if (!this.defaultHeaders['X-Requested-With']) {
2809                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2810                     }
2811                 }
2812
2813                 if(postData && this.useDefaultHeader){
2814                     this.initHeader('Content-Type', this.defaultPostHeader);
2815                 }
2816
2817                  if (this.hasDefaultHeaders || this.hasHeaders) {
2818                     this.setHeader(o);
2819                 }
2820
2821                 this.handleReadyState(o, callback);
2822                 o.conn.send(postData || null);
2823
2824                 return o;
2825             }
2826         },
2827
2828         handleReadyState:function(o, callback)
2829         {
2830             var oConn = this;
2831
2832             if (callback && callback.timeout) {
2833                 
2834                 this.timeout[o.tId] = window.setTimeout(function() {
2835                     oConn.abort(o, callback, true);
2836                 }, callback.timeout);
2837             }
2838
2839             this.poll[o.tId] = window.setInterval(
2840                     function() {
2841                         if (o.conn && o.conn.readyState == 4) {
2842                             window.clearInterval(oConn.poll[o.tId]);
2843                             delete oConn.poll[o.tId];
2844
2845                             if(callback && callback.timeout) {
2846                                 window.clearTimeout(oConn.timeout[o.tId]);
2847                                 delete oConn.timeout[o.tId];
2848                             }
2849
2850                             oConn.handleTransactionResponse(o, callback);
2851                         }
2852                     }
2853                     , this.pollInterval);
2854         },
2855
2856         handleTransactionResponse:function(o, callback, isAbort)
2857         {
2858
2859             if (!callback) {
2860                 this.releaseObject(o);
2861                 return;
2862             }
2863
2864             var httpStatus, responseObject;
2865
2866             try
2867             {
2868                 if (o.conn.status !== undefined && o.conn.status != 0) {
2869                     httpStatus = o.conn.status;
2870                 }
2871                 else {
2872                     httpStatus = 13030;
2873                 }
2874             }
2875             catch(e) {
2876
2877
2878                 httpStatus = 13030;
2879             }
2880
2881             if (httpStatus >= 200 && httpStatus < 300) {
2882                 responseObject = this.createResponseObject(o, callback.argument);
2883                 if (callback.success) {
2884                     if (!callback.scope) {
2885                         callback.success(responseObject);
2886                     }
2887                     else {
2888
2889
2890                         callback.success.apply(callback.scope, [responseObject]);
2891                     }
2892                 }
2893             }
2894             else {
2895                 switch (httpStatus) {
2896
2897                     case 12002:
2898                     case 12029:
2899                     case 12030:
2900                     case 12031:
2901                     case 12152:
2902                     case 13030:
2903                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2904                         if (callback.failure) {
2905                             if (!callback.scope) {
2906                                 callback.failure(responseObject);
2907                             }
2908                             else {
2909                                 callback.failure.apply(callback.scope, [responseObject]);
2910                             }
2911                         }
2912                         break;
2913                     default:
2914                         responseObject = this.createResponseObject(o, callback.argument);
2915                         if (callback.failure) {
2916                             if (!callback.scope) {
2917                                 callback.failure(responseObject);
2918                             }
2919                             else {
2920                                 callback.failure.apply(callback.scope, [responseObject]);
2921                             }
2922                         }
2923                 }
2924             }
2925
2926             this.releaseObject(o);
2927             responseObject = null;
2928         },
2929
2930         createResponseObject:function(o, callbackArg)
2931         {
2932             var obj = {};
2933             var headerObj = {};
2934
2935             try
2936             {
2937                 var headerStr = o.conn.getAllResponseHeaders();
2938                 var header = headerStr.split('\n');
2939                 for (var i = 0; i < header.length; i++) {
2940                     var delimitPos = header[i].indexOf(':');
2941                     if (delimitPos != -1) {
2942                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2943                     }
2944                 }
2945             }
2946             catch(e) {
2947             }
2948
2949             obj.tId = o.tId;
2950             obj.status = o.conn.status;
2951             obj.statusText = o.conn.statusText;
2952             obj.getResponseHeader = headerObj;
2953             obj.getAllResponseHeaders = headerStr;
2954             obj.responseText = o.conn.responseText;
2955             obj.responseXML = o.conn.responseXML;
2956
2957             if (typeof callbackArg !== undefined) {
2958                 obj.argument = callbackArg;
2959             }
2960
2961             return obj;
2962         },
2963
2964         createExceptionObject:function(tId, callbackArg, isAbort)
2965         {
2966             var COMM_CODE = 0;
2967             var COMM_ERROR = 'communication failure';
2968             var ABORT_CODE = -1;
2969             var ABORT_ERROR = 'transaction aborted';
2970
2971             var obj = {};
2972
2973             obj.tId = tId;
2974             if (isAbort) {
2975                 obj.status = ABORT_CODE;
2976                 obj.statusText = ABORT_ERROR;
2977             }
2978             else {
2979                 obj.status = COMM_CODE;
2980                 obj.statusText = COMM_ERROR;
2981             }
2982
2983             if (callbackArg) {
2984                 obj.argument = callbackArg;
2985             }
2986
2987             return obj;
2988         },
2989
2990         initHeader:function(label, value, isDefault)
2991         {
2992             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2993
2994             if (headerObj[label] === undefined) {
2995                 headerObj[label] = value;
2996             }
2997             else {
2998
2999
3000                 headerObj[label] = value + "," + headerObj[label];
3001             }
3002
3003             if (isDefault) {
3004                 this.hasDefaultHeaders = true;
3005             }
3006             else {
3007                 this.hasHeaders = true;
3008             }
3009         },
3010
3011
3012         setHeader:function(o)
3013         {
3014             if (this.hasDefaultHeaders) {
3015                 for (var prop in this.defaultHeaders) {
3016                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3017                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3018                     }
3019                 }
3020             }
3021
3022             if (this.hasHeaders) {
3023                 for (var prop in this.headers) {
3024                     if (this.headers.hasOwnProperty(prop)) {
3025                         o.conn.setRequestHeader(prop, this.headers[prop]);
3026                     }
3027                 }
3028                 this.headers = {};
3029                 this.hasHeaders = false;
3030             }
3031         },
3032
3033         resetDefaultHeaders:function() {
3034             delete this.defaultHeaders;
3035             this.defaultHeaders = {};
3036             this.hasDefaultHeaders = false;
3037         },
3038
3039         abort:function(o, callback, isTimeout)
3040         {
3041             if(this.isCallInProgress(o)) {
3042                 o.conn.abort();
3043                 window.clearInterval(this.poll[o.tId]);
3044                 delete this.poll[o.tId];
3045                 if (isTimeout) {
3046                     delete this.timeout[o.tId];
3047                 }
3048
3049                 this.handleTransactionResponse(o, callback, true);
3050
3051                 return true;
3052             }
3053             else {
3054                 return false;
3055             }
3056         },
3057
3058
3059         isCallInProgress:function(o)
3060         {
3061             if (o && o.conn) {
3062                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3063             }
3064             else {
3065
3066                 return false;
3067             }
3068         },
3069
3070
3071         releaseObject:function(o)
3072         {
3073
3074             o.conn = null;
3075
3076             o = null;
3077         },
3078
3079         activeX:[
3080         'MSXML2.XMLHTTP.3.0',
3081         'MSXML2.XMLHTTP',
3082         'Microsoft.XMLHTTP'
3083         ]
3084
3085
3086     };
3087 })();/*
3088  * Portions of this file are based on pieces of Yahoo User Interface Library
3089  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3090  * YUI licensed under the BSD License:
3091  * http://developer.yahoo.net/yui/license.txt
3092  * <script type="text/javascript">
3093  *
3094  */
3095
3096 Roo.lib.Region = function(t, r, b, l) {
3097     this.top = t;
3098     this[1] = t;
3099     this.right = r;
3100     this.bottom = b;
3101     this.left = l;
3102     this[0] = l;
3103 };
3104
3105
3106 Roo.lib.Region.prototype = {
3107     contains : function(region) {
3108         return ( region.left >= this.left &&
3109                  region.right <= this.right &&
3110                  region.top >= this.top &&
3111                  region.bottom <= this.bottom    );
3112
3113     },
3114
3115     getArea : function() {
3116         return ( (this.bottom - this.top) * (this.right - this.left) );
3117     },
3118
3119     intersect : function(region) {
3120         var t = Math.max(this.top, region.top);
3121         var r = Math.min(this.right, region.right);
3122         var b = Math.min(this.bottom, region.bottom);
3123         var l = Math.max(this.left, region.left);
3124
3125         if (b >= t && r >= l) {
3126             return new Roo.lib.Region(t, r, b, l);
3127         } else {
3128             return null;
3129         }
3130     },
3131     union : function(region) {
3132         var t = Math.min(this.top, region.top);
3133         var r = Math.max(this.right, region.right);
3134         var b = Math.max(this.bottom, region.bottom);
3135         var l = Math.min(this.left, region.left);
3136
3137         return new Roo.lib.Region(t, r, b, l);
3138     },
3139
3140     adjust : function(t, l, b, r) {
3141         this.top += t;
3142         this.left += l;
3143         this.right += r;
3144         this.bottom += b;
3145         return this;
3146     }
3147 };
3148
3149 Roo.lib.Region.getRegion = function(el) {
3150     var p = Roo.lib.Dom.getXY(el);
3151
3152     var t = p[1];
3153     var r = p[0] + el.offsetWidth;
3154     var b = p[1] + el.offsetHeight;
3155     var l = p[0];
3156
3157     return new Roo.lib.Region(t, r, b, l);
3158 };
3159 /*
3160  * Portions of this file are based on pieces of Yahoo User Interface Library
3161  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3162  * YUI licensed under the BSD License:
3163  * http://developer.yahoo.net/yui/license.txt
3164  * <script type="text/javascript">
3165  *
3166  */
3167 //@@dep Roo.lib.Region
3168
3169
3170 Roo.lib.Point = function(x, y) {
3171     if (x instanceof Array) {
3172         y = x[1];
3173         x = x[0];
3174     }
3175     this.x = this.right = this.left = this[0] = x;
3176     this.y = this.top = this.bottom = this[1] = y;
3177 };
3178
3179 Roo.lib.Point.prototype = new Roo.lib.Region();
3180 /*
3181  * Portions of this file are based on pieces of Yahoo User Interface Library
3182  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3183  * YUI licensed under the BSD License:
3184  * http://developer.yahoo.net/yui/license.txt
3185  * <script type="text/javascript">
3186  *
3187  */
3188  
3189 (function() {   
3190
3191     Roo.lib.Anim = {
3192         scroll : function(el, args, duration, easing, cb, scope) {
3193             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3194         },
3195
3196         motion : function(el, args, duration, easing, cb, scope) {
3197             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3198         },
3199
3200         color : function(el, args, duration, easing, cb, scope) {
3201             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3202         },
3203
3204         run : function(el, args, duration, easing, cb, scope, type) {
3205             type = type || Roo.lib.AnimBase;
3206             if (typeof easing == "string") {
3207                 easing = Roo.lib.Easing[easing];
3208             }
3209             var anim = new type(el, args, duration, easing);
3210             anim.animateX(function() {
3211                 Roo.callback(cb, scope);
3212             });
3213             return anim;
3214         }
3215     };
3216 })();/*
3217  * Portions of this file are based on pieces of Yahoo User Interface Library
3218  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3219  * YUI licensed under the BSD License:
3220  * http://developer.yahoo.net/yui/license.txt
3221  * <script type="text/javascript">
3222  *
3223  */
3224
3225 (function() {    
3226     var libFlyweight;
3227     
3228     function fly(el) {
3229         if (!libFlyweight) {
3230             libFlyweight = new Roo.Element.Flyweight();
3231         }
3232         libFlyweight.dom = el;
3233         return libFlyweight;
3234     }
3235
3236     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3237     
3238    
3239     
3240     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3241         if (el) {
3242             this.init(el, attributes, duration, method);
3243         }
3244     };
3245
3246     Roo.lib.AnimBase.fly = fly;
3247     
3248     
3249     
3250     Roo.lib.AnimBase.prototype = {
3251
3252         toString: function() {
3253             var el = this.getEl();
3254             var id = el.id || el.tagName;
3255             return ("Anim " + id);
3256         },
3257
3258         patterns: {
3259             noNegatives:        /width|height|opacity|padding/i,
3260             offsetAttribute:  /^((width|height)|(top|left))$/,
3261             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3262             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3263         },
3264
3265
3266         doMethod: function(attr, start, end) {
3267             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3268         },
3269
3270
3271         setAttribute: function(attr, val, unit) {
3272             if (this.patterns.noNegatives.test(attr)) {
3273                 val = (val > 0) ? val : 0;
3274             }
3275
3276             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3277         },
3278
3279
3280         getAttribute: function(attr) {
3281             var el = this.getEl();
3282             var val = fly(el).getStyle(attr);
3283
3284             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3285                 return parseFloat(val);
3286             }
3287
3288             var a = this.patterns.offsetAttribute.exec(attr) || [];
3289             var pos = !!( a[3] );
3290             var box = !!( a[2] );
3291
3292
3293             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3294                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3295             } else {
3296                 val = 0;
3297             }
3298
3299             return val;
3300         },
3301
3302
3303         getDefaultUnit: function(attr) {
3304             if (this.patterns.defaultUnit.test(attr)) {
3305                 return 'px';
3306             }
3307
3308             return '';
3309         },
3310
3311         animateX : function(callback, scope) {
3312             var f = function() {
3313                 this.onComplete.removeListener(f);
3314                 if (typeof callback == "function") {
3315                     callback.call(scope || this, this);
3316                 }
3317             };
3318             this.onComplete.addListener(f, this);
3319             this.animate();
3320         },
3321
3322
3323         setRuntimeAttribute: function(attr) {
3324             var start;
3325             var end;
3326             var attributes = this.attributes;
3327
3328             this.runtimeAttributes[attr] = {};
3329
3330             var isset = function(prop) {
3331                 return (typeof prop !== 'undefined');
3332             };
3333
3334             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3335                 return false;
3336             }
3337
3338             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3339
3340
3341             if (isset(attributes[attr]['to'])) {
3342                 end = attributes[attr]['to'];
3343             } else if (isset(attributes[attr]['by'])) {
3344                 if (start.constructor == Array) {
3345                     end = [];
3346                     for (var i = 0, len = start.length; i < len; ++i) {
3347                         end[i] = start[i] + attributes[attr]['by'][i];
3348                     }
3349                 } else {
3350                     end = start + attributes[attr]['by'];
3351                 }
3352             }
3353
3354             this.runtimeAttributes[attr].start = start;
3355             this.runtimeAttributes[attr].end = end;
3356
3357
3358             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3359         },
3360
3361
3362         init: function(el, attributes, duration, method) {
3363
3364             var isAnimated = false;
3365
3366
3367             var startTime = null;
3368
3369
3370             var actualFrames = 0;
3371
3372
3373             el = Roo.getDom(el);
3374
3375
3376             this.attributes = attributes || {};
3377
3378
3379             this.duration = duration || 1;
3380
3381
3382             this.method = method || Roo.lib.Easing.easeNone;
3383
3384
3385             this.useSeconds = true;
3386
3387
3388             this.currentFrame = 0;
3389
3390
3391             this.totalFrames = Roo.lib.AnimMgr.fps;
3392
3393
3394             this.getEl = function() {
3395                 return el;
3396             };
3397
3398
3399             this.isAnimated = function() {
3400                 return isAnimated;
3401             };
3402
3403
3404             this.getStartTime = function() {
3405                 return startTime;
3406             };
3407
3408             this.runtimeAttributes = {};
3409
3410
3411             this.animate = function() {
3412                 if (this.isAnimated()) {
3413                     return false;
3414                 }
3415
3416                 this.currentFrame = 0;
3417
3418                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3419
3420                 Roo.lib.AnimMgr.registerElement(this);
3421             };
3422
3423
3424             this.stop = function(finish) {
3425                 if (finish) {
3426                     this.currentFrame = this.totalFrames;
3427                     this._onTween.fire();
3428                 }
3429                 Roo.lib.AnimMgr.stop(this);
3430             };
3431
3432             var onStart = function() {
3433                 this.onStart.fire();
3434
3435                 this.runtimeAttributes = {};
3436                 for (var attr in this.attributes) {
3437                     this.setRuntimeAttribute(attr);
3438                 }
3439
3440                 isAnimated = true;
3441                 actualFrames = 0;
3442                 startTime = new Date();
3443             };
3444
3445
3446             var onTween = function() {
3447                 var data = {
3448                     duration: new Date() - this.getStartTime(),
3449                     currentFrame: this.currentFrame
3450                 };
3451
3452                 data.toString = function() {
3453                     return (
3454                             'duration: ' + data.duration +
3455                             ', currentFrame: ' + data.currentFrame
3456                             );
3457                 };
3458
3459                 this.onTween.fire(data);
3460
3461                 var runtimeAttributes = this.runtimeAttributes;
3462
3463                 for (var attr in runtimeAttributes) {
3464                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3465                 }
3466
3467                 actualFrames += 1;
3468             };
3469
3470             var onComplete = function() {
3471                 var actual_duration = (new Date() - startTime) / 1000 ;
3472
3473                 var data = {
3474                     duration: actual_duration,
3475                     frames: actualFrames,
3476                     fps: actualFrames / actual_duration
3477                 };
3478
3479                 data.toString = function() {
3480                     return (
3481                             'duration: ' + data.duration +
3482                             ', frames: ' + data.frames +
3483                             ', fps: ' + data.fps
3484                             );
3485                 };
3486
3487                 isAnimated = false;
3488                 actualFrames = 0;
3489                 this.onComplete.fire(data);
3490             };
3491
3492
3493             this._onStart = new Roo.util.Event(this);
3494             this.onStart = new Roo.util.Event(this);
3495             this.onTween = new Roo.util.Event(this);
3496             this._onTween = new Roo.util.Event(this);
3497             this.onComplete = new Roo.util.Event(this);
3498             this._onComplete = new Roo.util.Event(this);
3499             this._onStart.addListener(onStart);
3500             this._onTween.addListener(onTween);
3501             this._onComplete.addListener(onComplete);
3502         }
3503     };
3504 })();
3505 /*
3506  * Portions of this file are based on pieces of Yahoo User Interface Library
3507  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3508  * YUI licensed under the BSD License:
3509  * http://developer.yahoo.net/yui/license.txt
3510  * <script type="text/javascript">
3511  *
3512  */
3513
3514 Roo.lib.AnimMgr = new function() {
3515
3516         var thread = null;
3517
3518
3519         var queue = [];
3520
3521
3522         var tweenCount = 0;
3523
3524
3525         this.fps = 1000;
3526
3527
3528         this.delay = 1;
3529
3530
3531         this.registerElement = function(tween) {
3532             queue[queue.length] = tween;
3533             tweenCount += 1;
3534             tween._onStart.fire();
3535             this.start();
3536         };
3537
3538
3539         this.unRegister = function(tween, index) {
3540             tween._onComplete.fire();
3541             index = index || getIndex(tween);
3542             if (index != -1) {
3543                 queue.splice(index, 1);
3544             }
3545
3546             tweenCount -= 1;
3547             if (tweenCount <= 0) {
3548                 this.stop();
3549             }
3550         };
3551
3552
3553         this.start = function() {
3554             if (thread === null) {
3555                 thread = setInterval(this.run, this.delay);
3556             }
3557         };
3558
3559
3560         this.stop = function(tween) {
3561             if (!tween) {
3562                 clearInterval(thread);
3563
3564                 for (var i = 0, len = queue.length; i < len; ++i) {
3565                     if (queue[0].isAnimated()) {
3566                         this.unRegister(queue[0], 0);
3567                     }
3568                 }
3569
3570                 queue = [];
3571                 thread = null;
3572                 tweenCount = 0;
3573             }
3574             else {
3575                 this.unRegister(tween);
3576             }
3577         };
3578
3579
3580         this.run = function() {
3581             for (var i = 0, len = queue.length; i < len; ++i) {
3582                 var tween = queue[i];
3583                 if (!tween || !tween.isAnimated()) {
3584                     continue;
3585                 }
3586
3587                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3588                 {
3589                     tween.currentFrame += 1;
3590
3591                     if (tween.useSeconds) {
3592                         correctFrame(tween);
3593                     }
3594                     tween._onTween.fire();
3595                 }
3596                 else {
3597                     Roo.lib.AnimMgr.stop(tween, i);
3598                 }
3599             }
3600         };
3601
3602         var getIndex = function(anim) {
3603             for (var i = 0, len = queue.length; i < len; ++i) {
3604                 if (queue[i] == anim) {
3605                     return i;
3606                 }
3607             }
3608             return -1;
3609         };
3610
3611
3612         var correctFrame = function(tween) {
3613             var frames = tween.totalFrames;
3614             var frame = tween.currentFrame;
3615             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3616             var elapsed = (new Date() - tween.getStartTime());
3617             var tweak = 0;
3618
3619             if (elapsed < tween.duration * 1000) {
3620                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3621             } else {
3622                 tweak = frames - (frame + 1);
3623             }
3624             if (tweak > 0 && isFinite(tweak)) {
3625                 if (tween.currentFrame + tweak >= frames) {
3626                     tweak = frames - (frame + 1);
3627                 }
3628
3629                 tween.currentFrame += tweak;
3630             }
3631         };
3632     };/*
3633  * Portions of this file are based on pieces of Yahoo User Interface Library
3634  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3635  * YUI licensed under the BSD License:
3636  * http://developer.yahoo.net/yui/license.txt
3637  * <script type="text/javascript">
3638  *
3639  */
3640 Roo.lib.Bezier = new function() {
3641
3642         this.getPosition = function(points, t) {
3643             var n = points.length;
3644             var tmp = [];
3645
3646             for (var i = 0; i < n; ++i) {
3647                 tmp[i] = [points[i][0], points[i][1]];
3648             }
3649
3650             for (var j = 1; j < n; ++j) {
3651                 for (i = 0; i < n - j; ++i) {
3652                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3653                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3654                 }
3655             }
3656
3657             return [ tmp[0][0], tmp[0][1] ];
3658
3659         };
3660     };/*
3661  * Portions of this file are based on pieces of Yahoo User Interface Library
3662  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3663  * YUI licensed under the BSD License:
3664  * http://developer.yahoo.net/yui/license.txt
3665  * <script type="text/javascript">
3666  *
3667  */
3668 (function() {
3669
3670     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3671         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3672     };
3673
3674     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3675
3676     var fly = Roo.lib.AnimBase.fly;
3677     var Y = Roo.lib;
3678     var superclass = Y.ColorAnim.superclass;
3679     var proto = Y.ColorAnim.prototype;
3680
3681     proto.toString = function() {
3682         var el = this.getEl();
3683         var id = el.id || el.tagName;
3684         return ("ColorAnim " + id);
3685     };
3686
3687     proto.patterns.color = /color$/i;
3688     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3689     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3690     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3691     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3692
3693
3694     proto.parseColor = function(s) {
3695         if (s.length == 3) {
3696             return s;
3697         }
3698
3699         var c = this.patterns.hex.exec(s);
3700         if (c && c.length == 4) {
3701             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3702         }
3703
3704         c = this.patterns.rgb.exec(s);
3705         if (c && c.length == 4) {
3706             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3707         }
3708
3709         c = this.patterns.hex3.exec(s);
3710         if (c && c.length == 4) {
3711             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3712         }
3713
3714         return null;
3715     };
3716     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3717     proto.getAttribute = function(attr) {
3718         var el = this.getEl();
3719         if (this.patterns.color.test(attr)) {
3720             var val = fly(el).getStyle(attr);
3721
3722             if (this.patterns.transparent.test(val)) {
3723                 var parent = el.parentNode;
3724                 val = fly(parent).getStyle(attr);
3725
3726                 while (parent && this.patterns.transparent.test(val)) {
3727                     parent = parent.parentNode;
3728                     val = fly(parent).getStyle(attr);
3729                     if (parent.tagName.toUpperCase() == 'HTML') {
3730                         val = '#fff';
3731                     }
3732                 }
3733             }
3734         } else {
3735             val = superclass.getAttribute.call(this, attr);
3736         }
3737
3738         return val;
3739     };
3740     proto.getAttribute = function(attr) {
3741         var el = this.getEl();
3742         if (this.patterns.color.test(attr)) {
3743             var val = fly(el).getStyle(attr);
3744
3745             if (this.patterns.transparent.test(val)) {
3746                 var parent = el.parentNode;
3747                 val = fly(parent).getStyle(attr);
3748
3749                 while (parent && this.patterns.transparent.test(val)) {
3750                     parent = parent.parentNode;
3751                     val = fly(parent).getStyle(attr);
3752                     if (parent.tagName.toUpperCase() == 'HTML') {
3753                         val = '#fff';
3754                     }
3755                 }
3756             }
3757         } else {
3758             val = superclass.getAttribute.call(this, attr);
3759         }
3760
3761         return val;
3762     };
3763
3764     proto.doMethod = function(attr, start, end) {
3765         var val;
3766
3767         if (this.patterns.color.test(attr)) {
3768             val = [];
3769             for (var i = 0, len = start.length; i < len; ++i) {
3770                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3771             }
3772
3773             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3774         }
3775         else {
3776             val = superclass.doMethod.call(this, attr, start, end);
3777         }
3778
3779         return val;
3780     };
3781
3782     proto.setRuntimeAttribute = function(attr) {
3783         superclass.setRuntimeAttribute.call(this, attr);
3784
3785         if (this.patterns.color.test(attr)) {
3786             var attributes = this.attributes;
3787             var start = this.parseColor(this.runtimeAttributes[attr].start);
3788             var end = this.parseColor(this.runtimeAttributes[attr].end);
3789
3790             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3791                 end = this.parseColor(attributes[attr].by);
3792
3793                 for (var i = 0, len = start.length; i < len; ++i) {
3794                     end[i] = start[i] + end[i];
3795                 }
3796             }
3797
3798             this.runtimeAttributes[attr].start = start;
3799             this.runtimeAttributes[attr].end = end;
3800         }
3801     };
3802 })();
3803
3804 /*
3805  * Portions of this file are based on pieces of Yahoo User Interface Library
3806  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3807  * YUI licensed under the BSD License:
3808  * http://developer.yahoo.net/yui/license.txt
3809  * <script type="text/javascript">
3810  *
3811  */
3812 Roo.lib.Easing = {
3813
3814
3815     easeNone: function (t, b, c, d) {
3816         return c * t / d + b;
3817     },
3818
3819
3820     easeIn: function (t, b, c, d) {
3821         return c * (t /= d) * t + b;
3822     },
3823
3824
3825     easeOut: function (t, b, c, d) {
3826         return -c * (t /= d) * (t - 2) + b;
3827     },
3828
3829
3830     easeBoth: function (t, b, c, d) {
3831         if ((t /= d / 2) < 1) {
3832             return c / 2 * t * t + b;
3833         }
3834
3835         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3836     },
3837
3838
3839     easeInStrong: function (t, b, c, d) {
3840         return c * (t /= d) * t * t * t + b;
3841     },
3842
3843
3844     easeOutStrong: function (t, b, c, d) {
3845         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3846     },
3847
3848
3849     easeBothStrong: function (t, b, c, d) {
3850         if ((t /= d / 2) < 1) {
3851             return c / 2 * t * t * t * t + b;
3852         }
3853
3854         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3855     },
3856
3857
3858
3859     elasticIn: function (t, b, c, d, a, p) {
3860         if (t == 0) {
3861             return b;
3862         }
3863         if ((t /= d) == 1) {
3864             return b + c;
3865         }
3866         if (!p) {
3867             p = d * .3;
3868         }
3869
3870         if (!a || a < Math.abs(c)) {
3871             a = c;
3872             var s = p / 4;
3873         }
3874         else {
3875             var s = p / (2 * Math.PI) * Math.asin(c / a);
3876         }
3877
3878         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3879     },
3880
3881
3882     elasticOut: function (t, b, c, d, a, p) {
3883         if (t == 0) {
3884             return b;
3885         }
3886         if ((t /= d) == 1) {
3887             return b + c;
3888         }
3889         if (!p) {
3890             p = d * .3;
3891         }
3892
3893         if (!a || a < Math.abs(c)) {
3894             a = c;
3895             var s = p / 4;
3896         }
3897         else {
3898             var s = p / (2 * Math.PI) * Math.asin(c / a);
3899         }
3900
3901         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3902     },
3903
3904
3905     elasticBoth: function (t, b, c, d, a, p) {
3906         if (t == 0) {
3907             return b;
3908         }
3909
3910         if ((t /= d / 2) == 2) {
3911             return b + c;
3912         }
3913
3914         if (!p) {
3915             p = d * (.3 * 1.5);
3916         }
3917
3918         if (!a || a < Math.abs(c)) {
3919             a = c;
3920             var s = p / 4;
3921         }
3922         else {
3923             var s = p / (2 * Math.PI) * Math.asin(c / a);
3924         }
3925
3926         if (t < 1) {
3927             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3928                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3929         }
3930         return a * Math.pow(2, -10 * (t -= 1)) *
3931                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3932     },
3933
3934
3935
3936     backIn: function (t, b, c, d, s) {
3937         if (typeof s == 'undefined') {
3938             s = 1.70158;
3939         }
3940         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3941     },
3942
3943
3944     backOut: function (t, b, c, d, s) {
3945         if (typeof s == 'undefined') {
3946             s = 1.70158;
3947         }
3948         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3949     },
3950
3951
3952     backBoth: function (t, b, c, d, s) {
3953         if (typeof s == 'undefined') {
3954             s = 1.70158;
3955         }
3956
3957         if ((t /= d / 2 ) < 1) {
3958             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3959         }
3960         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3961     },
3962
3963
3964     bounceIn: function (t, b, c, d) {
3965         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3966     },
3967
3968
3969     bounceOut: function (t, b, c, d) {
3970         if ((t /= d) < (1 / 2.75)) {
3971             return c * (7.5625 * t * t) + b;
3972         } else if (t < (2 / 2.75)) {
3973             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3974         } else if (t < (2.5 / 2.75)) {
3975             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3976         }
3977         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3978     },
3979
3980
3981     bounceBoth: function (t, b, c, d) {
3982         if (t < d / 2) {
3983             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3984         }
3985         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3986     }
3987 };/*
3988  * Portions of this file are based on pieces of Yahoo User Interface Library
3989  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3990  * YUI licensed under the BSD License:
3991  * http://developer.yahoo.net/yui/license.txt
3992  * <script type="text/javascript">
3993  *
3994  */
3995     (function() {
3996         Roo.lib.Motion = function(el, attributes, duration, method) {
3997             if (el) {
3998                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3999             }
4000         };
4001
4002         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4003
4004
4005         var Y = Roo.lib;
4006         var superclass = Y.Motion.superclass;
4007         var proto = Y.Motion.prototype;
4008
4009         proto.toString = function() {
4010             var el = this.getEl();
4011             var id = el.id || el.tagName;
4012             return ("Motion " + id);
4013         };
4014
4015         proto.patterns.points = /^points$/i;
4016
4017         proto.setAttribute = function(attr, val, unit) {
4018             if (this.patterns.points.test(attr)) {
4019                 unit = unit || 'px';
4020                 superclass.setAttribute.call(this, 'left', val[0], unit);
4021                 superclass.setAttribute.call(this, 'top', val[1], unit);
4022             } else {
4023                 superclass.setAttribute.call(this, attr, val, unit);
4024             }
4025         };
4026
4027         proto.getAttribute = function(attr) {
4028             if (this.patterns.points.test(attr)) {
4029                 var val = [
4030                         superclass.getAttribute.call(this, 'left'),
4031                         superclass.getAttribute.call(this, 'top')
4032                         ];
4033             } else {
4034                 val = superclass.getAttribute.call(this, attr);
4035             }
4036
4037             return val;
4038         };
4039
4040         proto.doMethod = function(attr, start, end) {
4041             var val = null;
4042
4043             if (this.patterns.points.test(attr)) {
4044                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4045                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4046             } else {
4047                 val = superclass.doMethod.call(this, attr, start, end);
4048             }
4049             return val;
4050         };
4051
4052         proto.setRuntimeAttribute = function(attr) {
4053             if (this.patterns.points.test(attr)) {
4054                 var el = this.getEl();
4055                 var attributes = this.attributes;
4056                 var start;
4057                 var control = attributes['points']['control'] || [];
4058                 var end;
4059                 var i, len;
4060
4061                 if (control.length > 0 && !(control[0] instanceof Array)) {
4062                     control = [control];
4063                 } else {
4064                     var tmp = [];
4065                     for (i = 0,len = control.length; i < len; ++i) {
4066                         tmp[i] = control[i];
4067                     }
4068                     control = tmp;
4069                 }
4070
4071                 Roo.fly(el).position();
4072
4073                 if (isset(attributes['points']['from'])) {
4074                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4075                 }
4076                 else {
4077                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4078                 }
4079
4080                 start = this.getAttribute('points');
4081
4082
4083                 if (isset(attributes['points']['to'])) {
4084                     end = translateValues.call(this, attributes['points']['to'], start);
4085
4086                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4087                     for (i = 0,len = control.length; i < len; ++i) {
4088                         control[i] = translateValues.call(this, control[i], start);
4089                     }
4090
4091
4092                 } else if (isset(attributes['points']['by'])) {
4093                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4094
4095                     for (i = 0,len = control.length; i < len; ++i) {
4096                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4097                     }
4098                 }
4099
4100                 this.runtimeAttributes[attr] = [start];
4101
4102                 if (control.length > 0) {
4103                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4104                 }
4105
4106                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4107             }
4108             else {
4109                 superclass.setRuntimeAttribute.call(this, attr);
4110             }
4111         };
4112
4113         var translateValues = function(val, start) {
4114             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4115             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4116
4117             return val;
4118         };
4119
4120         var isset = function(prop) {
4121             return (typeof prop !== 'undefined');
4122         };
4123     })();
4124 /*
4125  * Portions of this file are based on pieces of Yahoo User Interface Library
4126  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4127  * YUI licensed under the BSD License:
4128  * http://developer.yahoo.net/yui/license.txt
4129  * <script type="text/javascript">
4130  *
4131  */
4132     (function() {
4133         Roo.lib.Scroll = function(el, attributes, duration, method) {
4134             if (el) {
4135                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4136             }
4137         };
4138
4139         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4140
4141
4142         var Y = Roo.lib;
4143         var superclass = Y.Scroll.superclass;
4144         var proto = Y.Scroll.prototype;
4145
4146         proto.toString = function() {
4147             var el = this.getEl();
4148             var id = el.id || el.tagName;
4149             return ("Scroll " + id);
4150         };
4151
4152         proto.doMethod = function(attr, start, end) {
4153             var val = null;
4154
4155             if (attr == 'scroll') {
4156                 val = [
4157                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4158                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4159                         ];
4160
4161             } else {
4162                 val = superclass.doMethod.call(this, attr, start, end);
4163             }
4164             return val;
4165         };
4166
4167         proto.getAttribute = function(attr) {
4168             var val = null;
4169             var el = this.getEl();
4170
4171             if (attr == 'scroll') {
4172                 val = [ el.scrollLeft, el.scrollTop ];
4173             } else {
4174                 val = superclass.getAttribute.call(this, attr);
4175             }
4176
4177             return val;
4178         };
4179
4180         proto.setAttribute = function(attr, val, unit) {
4181             var el = this.getEl();
4182
4183             if (attr == 'scroll') {
4184                 el.scrollLeft = val[0];
4185                 el.scrollTop = val[1];
4186             } else {
4187                 superclass.setAttribute.call(this, attr, val, unit);
4188             }
4189         };
4190     })();
4191 /*
4192  * Based on:
4193  * Ext JS Library 1.1.1
4194  * Copyright(c) 2006-2007, Ext JS, LLC.
4195  *
4196  * Originally Released Under LGPL - original licence link has changed is not relivant.
4197  *
4198  * Fork - LGPL
4199  * <script type="text/javascript">
4200  */
4201
4202
4203 // nasty IE9 hack - what a pile of crap that is..
4204
4205  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4206     Range.prototype.createContextualFragment = function (html) {
4207         var doc = window.document;
4208         var container = doc.createElement("div");
4209         container.innerHTML = html;
4210         var frag = doc.createDocumentFragment(), n;
4211         while ((n = container.firstChild)) {
4212             frag.appendChild(n);
4213         }
4214         return frag;
4215     };
4216 }
4217
4218 /**
4219  * @class Roo.DomHelper
4220  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4221  * 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>.
4222  * @singleton
4223  */
4224 Roo.DomHelper = function(){
4225     var tempTableEl = null;
4226     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4227     var tableRe = /^table|tbody|tr|td$/i;
4228     var xmlns = {};
4229     // build as innerHTML where available
4230     /** @ignore */
4231     var createHtml = function(o){
4232         if(typeof o == 'string'){
4233             return o;
4234         }
4235         var b = "";
4236         if(!o.tag){
4237             o.tag = "div";
4238         }
4239         b += "<" + o.tag;
4240         for(var attr in o){
4241             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4242             if(attr == "style"){
4243                 var s = o["style"];
4244                 if(typeof s == "function"){
4245                     s = s.call();
4246                 }
4247                 if(typeof s == "string"){
4248                     b += ' style="' + s + '"';
4249                 }else if(typeof s == "object"){
4250                     b += ' style="';
4251                     for(var key in s){
4252                         if(typeof s[key] != "function"){
4253                             b += key + ":" + s[key] + ";";
4254                         }
4255                     }
4256                     b += '"';
4257                 }
4258             }else{
4259                 if(attr == "cls"){
4260                     b += ' class="' + o["cls"] + '"';
4261                 }else if(attr == "htmlFor"){
4262                     b += ' for="' + o["htmlFor"] + '"';
4263                 }else{
4264                     b += " " + attr + '="' + o[attr] + '"';
4265                 }
4266             }
4267         }
4268         if(emptyTags.test(o.tag)){
4269             b += "/>";
4270         }else{
4271             b += ">";
4272             var cn = o.children || o.cn;
4273             if(cn){
4274                 //http://bugs.kde.org/show_bug.cgi?id=71506
4275                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4276                     for(var i = 0, len = cn.length; i < len; i++) {
4277                         b += createHtml(cn[i], b);
4278                     }
4279                 }else{
4280                     b += createHtml(cn, b);
4281                 }
4282             }
4283             if(o.html){
4284                 b += o.html;
4285             }
4286             b += "</" + o.tag + ">";
4287         }
4288         return b;
4289     };
4290
4291     // build as dom
4292     /** @ignore */
4293     var createDom = function(o, parentNode){
4294          
4295         // defininition craeted..
4296         var ns = false;
4297         if (o.ns && o.ns != 'html') {
4298                
4299             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4300                 xmlns[o.ns] = o.xmlns;
4301                 ns = o.xmlns;
4302             }
4303             if (typeof(xmlns[o.ns]) == 'undefined') {
4304                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4305             }
4306             ns = xmlns[o.ns];
4307         }
4308         
4309         
4310         if (typeof(o) == 'string') {
4311             return parentNode.appendChild(document.createTextNode(o));
4312         }
4313         o.tag = o.tag || div;
4314         if (o.ns && Roo.isIE) {
4315             ns = false;
4316             o.tag = o.ns + ':' + o.tag;
4317             
4318         }
4319         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4320         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4321         for(var attr in o){
4322             
4323             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4324                     attr == "style" || typeof o[attr] == "function") continue;
4325                     
4326             if(attr=="cls" && Roo.isIE){
4327                 el.className = o["cls"];
4328             }else{
4329                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4330                 else el[attr] = o[attr];
4331             }
4332         }
4333         Roo.DomHelper.applyStyles(el, o.style);
4334         var cn = o.children || o.cn;
4335         if(cn){
4336             //http://bugs.kde.org/show_bug.cgi?id=71506
4337              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4338                 for(var i = 0, len = cn.length; i < len; i++) {
4339                     createDom(cn[i], el);
4340                 }
4341             }else{
4342                 createDom(cn, el);
4343             }
4344         }
4345         if(o.html){
4346             el.innerHTML = o.html;
4347         }
4348         if(parentNode){
4349            parentNode.appendChild(el);
4350         }
4351         return el;
4352     };
4353
4354     var ieTable = function(depth, s, h, e){
4355         tempTableEl.innerHTML = [s, h, e].join('');
4356         var i = -1, el = tempTableEl;
4357         while(++i < depth){
4358             el = el.firstChild;
4359         }
4360         return el;
4361     };
4362
4363     // kill repeat to save bytes
4364     var ts = '<table>',
4365         te = '</table>',
4366         tbs = ts+'<tbody>',
4367         tbe = '</tbody>'+te,
4368         trs = tbs + '<tr>',
4369         tre = '</tr>'+tbe;
4370
4371     /**
4372      * @ignore
4373      * Nasty code for IE's broken table implementation
4374      */
4375     var insertIntoTable = function(tag, where, el, html){
4376         if(!tempTableEl){
4377             tempTableEl = document.createElement('div');
4378         }
4379         var node;
4380         var before = null;
4381         if(tag == 'td'){
4382             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4383                 return;
4384             }
4385             if(where == 'beforebegin'){
4386                 before = el;
4387                 el = el.parentNode;
4388             } else{
4389                 before = el.nextSibling;
4390                 el = el.parentNode;
4391             }
4392             node = ieTable(4, trs, html, tre);
4393         }
4394         else if(tag == 'tr'){
4395             if(where == 'beforebegin'){
4396                 before = el;
4397                 el = el.parentNode;
4398                 node = ieTable(3, tbs, html, tbe);
4399             } else if(where == 'afterend'){
4400                 before = el.nextSibling;
4401                 el = el.parentNode;
4402                 node = ieTable(3, tbs, html, tbe);
4403             } else{ // INTO a TR
4404                 if(where == 'afterbegin'){
4405                     before = el.firstChild;
4406                 }
4407                 node = ieTable(4, trs, html, tre);
4408             }
4409         } else if(tag == 'tbody'){
4410             if(where == 'beforebegin'){
4411                 before = el;
4412                 el = el.parentNode;
4413                 node = ieTable(2, ts, html, te);
4414             } else if(where == 'afterend'){
4415                 before = el.nextSibling;
4416                 el = el.parentNode;
4417                 node = ieTable(2, ts, html, te);
4418             } else{
4419                 if(where == 'afterbegin'){
4420                     before = el.firstChild;
4421                 }
4422                 node = ieTable(3, tbs, html, tbe);
4423             }
4424         } else{ // TABLE
4425             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4426                 return;
4427             }
4428             if(where == 'afterbegin'){
4429                 before = el.firstChild;
4430             }
4431             node = ieTable(2, ts, html, te);
4432         }
4433         el.insertBefore(node, before);
4434         return node;
4435     };
4436
4437     return {
4438     /** True to force the use of DOM instead of html fragments @type Boolean */
4439     useDom : false,
4440
4441     /**
4442      * Returns the markup for the passed Element(s) config
4443      * @param {Object} o The Dom object spec (and children)
4444      * @return {String}
4445      */
4446     markup : function(o){
4447         return createHtml(o);
4448     },
4449
4450     /**
4451      * Applies a style specification to an element
4452      * @param {String/HTMLElement} el The element to apply styles to
4453      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4454      * a function which returns such a specification.
4455      */
4456     applyStyles : function(el, styles){
4457         if(styles){
4458            el = Roo.fly(el);
4459            if(typeof styles == "string"){
4460                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4461                var matches;
4462                while ((matches = re.exec(styles)) != null){
4463                    el.setStyle(matches[1], matches[2]);
4464                }
4465            }else if (typeof styles == "object"){
4466                for (var style in styles){
4467                   el.setStyle(style, styles[style]);
4468                }
4469            }else if (typeof styles == "function"){
4470                 Roo.DomHelper.applyStyles(el, styles.call());
4471            }
4472         }
4473     },
4474
4475     /**
4476      * Inserts an HTML fragment into the Dom
4477      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4478      * @param {HTMLElement} el The context element
4479      * @param {String} html The HTML fragmenet
4480      * @return {HTMLElement} The new node
4481      */
4482     insertHtml : function(where, el, html){
4483         where = where.toLowerCase();
4484         if(el.insertAdjacentHTML){
4485             if(tableRe.test(el.tagName)){
4486                 var rs;
4487                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4488                     return rs;
4489                 }
4490             }
4491             switch(where){
4492                 case "beforebegin":
4493                     el.insertAdjacentHTML('BeforeBegin', html);
4494                     return el.previousSibling;
4495                 case "afterbegin":
4496                     el.insertAdjacentHTML('AfterBegin', html);
4497                     return el.firstChild;
4498                 case "beforeend":
4499                     el.insertAdjacentHTML('BeforeEnd', html);
4500                     return el.lastChild;
4501                 case "afterend":
4502                     el.insertAdjacentHTML('AfterEnd', html);
4503                     return el.nextSibling;
4504             }
4505             throw 'Illegal insertion point -> "' + where + '"';
4506         }
4507         var range = el.ownerDocument.createRange();
4508         var frag;
4509         switch(where){
4510              case "beforebegin":
4511                 range.setStartBefore(el);
4512                 frag = range.createContextualFragment(html);
4513                 el.parentNode.insertBefore(frag, el);
4514                 return el.previousSibling;
4515              case "afterbegin":
4516                 if(el.firstChild){
4517                     range.setStartBefore(el.firstChild);
4518                     frag = range.createContextualFragment(html);
4519                     el.insertBefore(frag, el.firstChild);
4520                     return el.firstChild;
4521                 }else{
4522                     el.innerHTML = html;
4523                     return el.firstChild;
4524                 }
4525             case "beforeend":
4526                 if(el.lastChild){
4527                     range.setStartAfter(el.lastChild);
4528                     frag = range.createContextualFragment(html);
4529                     el.appendChild(frag);
4530                     return el.lastChild;
4531                 }else{
4532                     el.innerHTML = html;
4533                     return el.lastChild;
4534                 }
4535             case "afterend":
4536                 range.setStartAfter(el);
4537                 frag = range.createContextualFragment(html);
4538                 el.parentNode.insertBefore(frag, el.nextSibling);
4539                 return el.nextSibling;
4540             }
4541             throw 'Illegal insertion point -> "' + where + '"';
4542     },
4543
4544     /**
4545      * Creates new Dom element(s) and inserts them before el
4546      * @param {String/HTMLElement/Element} el The context element
4547      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4548      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4549      * @return {HTMLElement/Roo.Element} The new node
4550      */
4551     insertBefore : function(el, o, returnElement){
4552         return this.doInsert(el, o, returnElement, "beforeBegin");
4553     },
4554
4555     /**
4556      * Creates new Dom element(s) and inserts them after el
4557      * @param {String/HTMLElement/Element} el The context element
4558      * @param {Object} o The Dom object spec (and children)
4559      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4560      * @return {HTMLElement/Roo.Element} The new node
4561      */
4562     insertAfter : function(el, o, returnElement){
4563         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4564     },
4565
4566     /**
4567      * Creates new Dom element(s) and inserts them as the first child of el
4568      * @param {String/HTMLElement/Element} el The context element
4569      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4570      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4571      * @return {HTMLElement/Roo.Element} The new node
4572      */
4573     insertFirst : function(el, o, returnElement){
4574         return this.doInsert(el, o, returnElement, "afterBegin");
4575     },
4576
4577     // private
4578     doInsert : function(el, o, returnElement, pos, sibling){
4579         el = Roo.getDom(el);
4580         var newNode;
4581         if(this.useDom || o.ns){
4582             newNode = createDom(o, null);
4583             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4584         }else{
4585             var html = createHtml(o);
4586             newNode = this.insertHtml(pos, el, html);
4587         }
4588         return returnElement ? Roo.get(newNode, true) : newNode;
4589     },
4590
4591     /**
4592      * Creates new Dom element(s) and appends them to el
4593      * @param {String/HTMLElement/Element} el The context element
4594      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4595      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4596      * @return {HTMLElement/Roo.Element} The new node
4597      */
4598     append : function(el, o, returnElement){
4599         el = Roo.getDom(el);
4600         var newNode;
4601         if(this.useDom || o.ns){
4602             newNode = createDom(o, null);
4603             el.appendChild(newNode);
4604         }else{
4605             var html = createHtml(o);
4606             newNode = this.insertHtml("beforeEnd", el, html);
4607         }
4608         return returnElement ? Roo.get(newNode, true) : newNode;
4609     },
4610
4611     /**
4612      * Creates new Dom element(s) and overwrites the contents of el with them
4613      * @param {String/HTMLElement/Element} el The context element
4614      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4615      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4616      * @return {HTMLElement/Roo.Element} The new node
4617      */
4618     overwrite : function(el, o, returnElement){
4619         el = Roo.getDom(el);
4620         if (o.ns) {
4621           
4622             while (el.childNodes.length) {
4623                 el.removeChild(el.firstChild);
4624             }
4625             createDom(o, el);
4626         } else {
4627             el.innerHTML = createHtml(o);   
4628         }
4629         
4630         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4631     },
4632
4633     /**
4634      * Creates a new Roo.DomHelper.Template from the Dom object spec
4635      * @param {Object} o The Dom object spec (and children)
4636      * @return {Roo.DomHelper.Template} The new template
4637      */
4638     createTemplate : function(o){
4639         var html = createHtml(o);
4640         return new Roo.Template(html);
4641     }
4642     };
4643 }();
4644 /*
4645  * Based on:
4646  * Ext JS Library 1.1.1
4647  * Copyright(c) 2006-2007, Ext JS, LLC.
4648  *
4649  * Originally Released Under LGPL - original licence link has changed is not relivant.
4650  *
4651  * Fork - LGPL
4652  * <script type="text/javascript">
4653  */
4654  
4655 /**
4656 * @class Roo.Template
4657 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4658 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4659 * Usage:
4660 <pre><code>
4661 var t = new Roo.Template({
4662     html :  '&lt;div name="{id}"&gt;' + 
4663         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4664         '&lt;/div&gt;',
4665     myformat: function (value, allValues) {
4666         return 'XX' + value;
4667     }
4668 });
4669 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4670 </code></pre>
4671 * For more information see this blog post with examples:
4672 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4673      - Create Elements using DOM, HTML fragments and Templates</a>. 
4674 * @constructor
4675 * @param {Object} cfg - Configuration object.
4676 */
4677 Roo.Template = function(cfg){
4678     // BC!
4679     if(cfg instanceof Array){
4680         cfg = cfg.join("");
4681     }else if(arguments.length > 1){
4682         cfg = Array.prototype.join.call(arguments, "");
4683     }
4684     
4685     
4686     if (typeof(cfg) == 'object') {
4687         Roo.apply(this,cfg)
4688     } else {
4689         // bc
4690         this.html = cfg;
4691     }
4692     if (this.url) {
4693         this.load();
4694     }
4695     
4696 };
4697 Roo.Template.prototype = {
4698     
4699     /**
4700      * @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..
4701      *                    it should be fixed so that template is observable...
4702      */
4703     url : false,
4704     /**
4705      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4706      */
4707     html : '',
4708     /**
4709      * Returns an HTML fragment of this template with the specified values applied.
4710      * @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'})
4711      * @return {String} The HTML fragment
4712      */
4713     applyTemplate : function(values){
4714         try {
4715            
4716             if(this.compiled){
4717                 return this.compiled(values);
4718             }
4719             var useF = this.disableFormats !== true;
4720             var fm = Roo.util.Format, tpl = this;
4721             var fn = function(m, name, format, args){
4722                 if(format && useF){
4723                     if(format.substr(0, 5) == "this."){
4724                         return tpl.call(format.substr(5), values[name], values);
4725                     }else{
4726                         if(args){
4727                             // quoted values are required for strings in compiled templates, 
4728                             // but for non compiled we need to strip them
4729                             // quoted reversed for jsmin
4730                             var re = /^\s*['"](.*)["']\s*$/;
4731                             args = args.split(',');
4732                             for(var i = 0, len = args.length; i < len; i++){
4733                                 args[i] = args[i].replace(re, "$1");
4734                             }
4735                             args = [values[name]].concat(args);
4736                         }else{
4737                             args = [values[name]];
4738                         }
4739                         return fm[format].apply(fm, args);
4740                     }
4741                 }else{
4742                     return values[name] !== undefined ? values[name] : "";
4743                 }
4744             };
4745             return this.html.replace(this.re, fn);
4746         } catch (e) {
4747             Roo.log(e);
4748             throw e;
4749         }
4750          
4751     },
4752     
4753     loading : false,
4754       
4755     load : function ()
4756     {
4757          
4758         if (this.loading) {
4759             return;
4760         }
4761         var _t = this;
4762         
4763         this.loading = true;
4764         this.compiled = false;
4765         
4766         var cx = new Roo.data.Connection();
4767         cx.request({
4768             url : this.url,
4769             method : 'GET',
4770             success : function (response) {
4771                 _t.loading = false;
4772                 _t.html = response.responseText;
4773                 _t.url = false;
4774                 _t.compile();
4775              },
4776             failure : function(response) {
4777                 Roo.log("Template failed to load from " + _t.url);
4778                 _t.loading = false;
4779             }
4780         });
4781     },
4782
4783     /**
4784      * Sets the HTML used as the template and optionally compiles it.
4785      * @param {String} html
4786      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4787      * @return {Roo.Template} this
4788      */
4789     set : function(html, compile){
4790         this.html = html;
4791         this.compiled = null;
4792         if(compile){
4793             this.compile();
4794         }
4795         return this;
4796     },
4797     
4798     /**
4799      * True to disable format functions (defaults to false)
4800      * @type Boolean
4801      */
4802     disableFormats : false,
4803     
4804     /**
4805     * The regular expression used to match template variables 
4806     * @type RegExp
4807     * @property 
4808     */
4809     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4810     
4811     /**
4812      * Compiles the template into an internal function, eliminating the RegEx overhead.
4813      * @return {Roo.Template} this
4814      */
4815     compile : function(){
4816         var fm = Roo.util.Format;
4817         var useF = this.disableFormats !== true;
4818         var sep = Roo.isGecko ? "+" : ",";
4819         var fn = function(m, name, format, args){
4820             if(format && useF){
4821                 args = args ? ',' + args : "";
4822                 if(format.substr(0, 5) != "this."){
4823                     format = "fm." + format + '(';
4824                 }else{
4825                     format = 'this.call("'+ format.substr(5) + '", ';
4826                     args = ", values";
4827                 }
4828             }else{
4829                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4830             }
4831             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4832         };
4833         var body;
4834         // branched to use + in gecko and [].join() in others
4835         if(Roo.isGecko){
4836             body = "this.compiled = function(values){ return '" +
4837                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4838                     "';};";
4839         }else{
4840             body = ["this.compiled = function(values){ return ['"];
4841             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4842             body.push("'].join('');};");
4843             body = body.join('');
4844         }
4845         /**
4846          * eval:var:values
4847          * eval:var:fm
4848          */
4849         eval(body);
4850         return this;
4851     },
4852     
4853     // private function used to call members
4854     call : function(fnName, value, allValues){
4855         return this[fnName](value, allValues);
4856     },
4857     
4858     /**
4859      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4860      * @param {String/HTMLElement/Roo.Element} el The context element
4861      * @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'})
4862      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4863      * @return {HTMLElement/Roo.Element} The new node or Element
4864      */
4865     insertFirst: function(el, values, returnElement){
4866         return this.doInsert('afterBegin', el, values, returnElement);
4867     },
4868
4869     /**
4870      * Applies the supplied values to the template and inserts the new node(s) before el.
4871      * @param {String/HTMLElement/Roo.Element} el The context element
4872      * @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'})
4873      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4874      * @return {HTMLElement/Roo.Element} The new node or Element
4875      */
4876     insertBefore: function(el, values, returnElement){
4877         return this.doInsert('beforeBegin', el, values, returnElement);
4878     },
4879
4880     /**
4881      * Applies the supplied values to the template and inserts the new node(s) after el.
4882      * @param {String/HTMLElement/Roo.Element} el The context element
4883      * @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'})
4884      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4885      * @return {HTMLElement/Roo.Element} The new node or Element
4886      */
4887     insertAfter : function(el, values, returnElement){
4888         return this.doInsert('afterEnd', el, values, returnElement);
4889     },
4890     
4891     /**
4892      * Applies the supplied values to the template and appends the new node(s) to el.
4893      * @param {String/HTMLElement/Roo.Element} el The context element
4894      * @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'})
4895      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4896      * @return {HTMLElement/Roo.Element} The new node or Element
4897      */
4898     append : function(el, values, returnElement){
4899         return this.doInsert('beforeEnd', el, values, returnElement);
4900     },
4901
4902     doInsert : function(where, el, values, returnEl){
4903         el = Roo.getDom(el);
4904         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4905         return returnEl ? Roo.get(newNode, true) : newNode;
4906     },
4907
4908     /**
4909      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4910      * @param {String/HTMLElement/Roo.Element} el The context element
4911      * @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'})
4912      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4913      * @return {HTMLElement/Roo.Element} The new node or Element
4914      */
4915     overwrite : function(el, values, returnElement){
4916         el = Roo.getDom(el);
4917         el.innerHTML = this.applyTemplate(values);
4918         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4919     }
4920 };
4921 /**
4922  * Alias for {@link #applyTemplate}
4923  * @method
4924  */
4925 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4926
4927 // backwards compat
4928 Roo.DomHelper.Template = Roo.Template;
4929
4930 /**
4931  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4932  * @param {String/HTMLElement} el A DOM element or its id
4933  * @returns {Roo.Template} The created template
4934  * @static
4935  */
4936 Roo.Template.from = function(el){
4937     el = Roo.getDom(el);
4938     return new Roo.Template(el.value || el.innerHTML);
4939 };/*
4940  * Based on:
4941  * Ext JS Library 1.1.1
4942  * Copyright(c) 2006-2007, Ext JS, LLC.
4943  *
4944  * Originally Released Under LGPL - original licence link has changed is not relivant.
4945  *
4946  * Fork - LGPL
4947  * <script type="text/javascript">
4948  */
4949  
4950
4951 /*
4952  * This is code is also distributed under MIT license for use
4953  * with jQuery and prototype JavaScript libraries.
4954  */
4955 /**
4956  * @class Roo.DomQuery
4957 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).
4958 <p>
4959 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>
4960
4961 <p>
4962 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.
4963 </p>
4964 <h4>Element Selectors:</h4>
4965 <ul class="list">
4966     <li> <b>*</b> any element</li>
4967     <li> <b>E</b> an element with the tag E</li>
4968     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4969     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4970     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4971     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4972 </ul>
4973 <h4>Attribute Selectors:</h4>
4974 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4975 <ul class="list">
4976     <li> <b>E[foo]</b> has an attribute "foo"</li>
4977     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4978     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4979     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4980     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4981     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4982     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4983 </ul>
4984 <h4>Pseudo Classes:</h4>
4985 <ul class="list">
4986     <li> <b>E:first-child</b> E is the first child of its parent</li>
4987     <li> <b>E:last-child</b> E is the last child of its parent</li>
4988     <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>
4989     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4990     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4991     <li> <b>E:only-child</b> E is the only child of its parent</li>
4992     <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>
4993     <li> <b>E:first</b> the first E in the resultset</li>
4994     <li> <b>E:last</b> the last E in the resultset</li>
4995     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4996     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4997     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4998     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4999     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5000     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5001     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5002     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5003     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5004 </ul>
5005 <h4>CSS Value Selectors:</h4>
5006 <ul class="list">
5007     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5008     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5009     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5010     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5011     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5012     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5013 </ul>
5014  * @singleton
5015  */
5016 Roo.DomQuery = function(){
5017     var cache = {}, simpleCache = {}, valueCache = {};
5018     var nonSpace = /\S/;
5019     var trimRe = /^\s+|\s+$/g;
5020     var tplRe = /\{(\d+)\}/g;
5021     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5022     var tagTokenRe = /^(#)?([\w-\*]+)/;
5023     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5024
5025     function child(p, index){
5026         var i = 0;
5027         var n = p.firstChild;
5028         while(n){
5029             if(n.nodeType == 1){
5030                if(++i == index){
5031                    return n;
5032                }
5033             }
5034             n = n.nextSibling;
5035         }
5036         return null;
5037     };
5038
5039     function next(n){
5040         while((n = n.nextSibling) && n.nodeType != 1);
5041         return n;
5042     };
5043
5044     function prev(n){
5045         while((n = n.previousSibling) && n.nodeType != 1);
5046         return n;
5047     };
5048
5049     function children(d){
5050         var n = d.firstChild, ni = -1;
5051             while(n){
5052                 var nx = n.nextSibling;
5053                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5054                     d.removeChild(n);
5055                 }else{
5056                     n.nodeIndex = ++ni;
5057                 }
5058                 n = nx;
5059             }
5060             return this;
5061         };
5062
5063     function byClassName(c, a, v){
5064         if(!v){
5065             return c;
5066         }
5067         var r = [], ri = -1, cn;
5068         for(var i = 0, ci; ci = c[i]; i++){
5069             if((' '+ci.className+' ').indexOf(v) != -1){
5070                 r[++ri] = ci;
5071             }
5072         }
5073         return r;
5074     };
5075
5076     function attrValue(n, attr){
5077         if(!n.tagName && typeof n.length != "undefined"){
5078             n = n[0];
5079         }
5080         if(!n){
5081             return null;
5082         }
5083         if(attr == "for"){
5084             return n.htmlFor;
5085         }
5086         if(attr == "class" || attr == "className"){
5087             return n.className;
5088         }
5089         return n.getAttribute(attr) || n[attr];
5090
5091     };
5092
5093     function getNodes(ns, mode, tagName){
5094         var result = [], ri = -1, cs;
5095         if(!ns){
5096             return result;
5097         }
5098         tagName = tagName || "*";
5099         if(typeof ns.getElementsByTagName != "undefined"){
5100             ns = [ns];
5101         }
5102         if(!mode){
5103             for(var i = 0, ni; ni = ns[i]; i++){
5104                 cs = ni.getElementsByTagName(tagName);
5105                 for(var j = 0, ci; ci = cs[j]; j++){
5106                     result[++ri] = ci;
5107                 }
5108             }
5109         }else if(mode == "/" || mode == ">"){
5110             var utag = tagName.toUpperCase();
5111             for(var i = 0, ni, cn; ni = ns[i]; i++){
5112                 cn = ni.children || ni.childNodes;
5113                 for(var j = 0, cj; cj = cn[j]; j++){
5114                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5115                         result[++ri] = cj;
5116                     }
5117                 }
5118             }
5119         }else if(mode == "+"){
5120             var utag = tagName.toUpperCase();
5121             for(var i = 0, n; n = ns[i]; i++){
5122                 while((n = n.nextSibling) && n.nodeType != 1);
5123                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5124                     result[++ri] = n;
5125                 }
5126             }
5127         }else if(mode == "~"){
5128             for(var i = 0, n; n = ns[i]; i++){
5129                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5130                 if(n){
5131                     result[++ri] = n;
5132                 }
5133             }
5134         }
5135         return result;
5136     };
5137
5138     function concat(a, b){
5139         if(b.slice){
5140             return a.concat(b);
5141         }
5142         for(var i = 0, l = b.length; i < l; i++){
5143             a[a.length] = b[i];
5144         }
5145         return a;
5146     }
5147
5148     function byTag(cs, tagName){
5149         if(cs.tagName || cs == document){
5150             cs = [cs];
5151         }
5152         if(!tagName){
5153             return cs;
5154         }
5155         var r = [], ri = -1;
5156         tagName = tagName.toLowerCase();
5157         for(var i = 0, ci; ci = cs[i]; i++){
5158             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5159                 r[++ri] = ci;
5160             }
5161         }
5162         return r;
5163     };
5164
5165     function byId(cs, attr, id){
5166         if(cs.tagName || cs == document){
5167             cs = [cs];
5168         }
5169         if(!id){
5170             return cs;
5171         }
5172         var r = [], ri = -1;
5173         for(var i = 0,ci; ci = cs[i]; i++){
5174             if(ci && ci.id == id){
5175                 r[++ri] = ci;
5176                 return r;
5177             }
5178         }
5179         return r;
5180     };
5181
5182     function byAttribute(cs, attr, value, op, custom){
5183         var r = [], ri = -1, st = custom=="{";
5184         var f = Roo.DomQuery.operators[op];
5185         for(var i = 0, ci; ci = cs[i]; i++){
5186             var a;
5187             if(st){
5188                 a = Roo.DomQuery.getStyle(ci, attr);
5189             }
5190             else if(attr == "class" || attr == "className"){
5191                 a = ci.className;
5192             }else if(attr == "for"){
5193                 a = ci.htmlFor;
5194             }else if(attr == "href"){
5195                 a = ci.getAttribute("href", 2);
5196             }else{
5197                 a = ci.getAttribute(attr);
5198             }
5199             if((f && f(a, value)) || (!f && a)){
5200                 r[++ri] = ci;
5201             }
5202         }
5203         return r;
5204     };
5205
5206     function byPseudo(cs, name, value){
5207         return Roo.DomQuery.pseudos[name](cs, value);
5208     };
5209
5210     // This is for IE MSXML which does not support expandos.
5211     // IE runs the same speed using setAttribute, however FF slows way down
5212     // and Safari completely fails so they need to continue to use expandos.
5213     var isIE = window.ActiveXObject ? true : false;
5214
5215     // this eval is stop the compressor from
5216     // renaming the variable to something shorter
5217     
5218     /** eval:var:batch */
5219     var batch = 30803; 
5220
5221     var key = 30803;
5222
5223     function nodupIEXml(cs){
5224         var d = ++key;
5225         cs[0].setAttribute("_nodup", d);
5226         var r = [cs[0]];
5227         for(var i = 1, len = cs.length; i < len; i++){
5228             var c = cs[i];
5229             if(!c.getAttribute("_nodup") != d){
5230                 c.setAttribute("_nodup", d);
5231                 r[r.length] = c;
5232             }
5233         }
5234         for(var i = 0, len = cs.length; i < len; i++){
5235             cs[i].removeAttribute("_nodup");
5236         }
5237         return r;
5238     }
5239
5240     function nodup(cs){
5241         if(!cs){
5242             return [];
5243         }
5244         var len = cs.length, c, i, r = cs, cj, ri = -1;
5245         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5246             return cs;
5247         }
5248         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5249             return nodupIEXml(cs);
5250         }
5251         var d = ++key;
5252         cs[0]._nodup = d;
5253         for(i = 1; c = cs[i]; i++){
5254             if(c._nodup != d){
5255                 c._nodup = d;
5256             }else{
5257                 r = [];
5258                 for(var j = 0; j < i; j++){
5259                     r[++ri] = cs[j];
5260                 }
5261                 for(j = i+1; cj = cs[j]; j++){
5262                     if(cj._nodup != d){
5263                         cj._nodup = d;
5264                         r[++ri] = cj;
5265                     }
5266                 }
5267                 return r;
5268             }
5269         }
5270         return r;
5271     }
5272
5273     function quickDiffIEXml(c1, c2){
5274         var d = ++key;
5275         for(var i = 0, len = c1.length; i < len; i++){
5276             c1[i].setAttribute("_qdiff", d);
5277         }
5278         var r = [];
5279         for(var i = 0, len = c2.length; i < len; i++){
5280             if(c2[i].getAttribute("_qdiff") != d){
5281                 r[r.length] = c2[i];
5282             }
5283         }
5284         for(var i = 0, len = c1.length; i < len; i++){
5285            c1[i].removeAttribute("_qdiff");
5286         }
5287         return r;
5288     }
5289
5290     function quickDiff(c1, c2){
5291         var len1 = c1.length;
5292         if(!len1){
5293             return c2;
5294         }
5295         if(isIE && c1[0].selectSingleNode){
5296             return quickDiffIEXml(c1, c2);
5297         }
5298         var d = ++key;
5299         for(var i = 0; i < len1; i++){
5300             c1[i]._qdiff = d;
5301         }
5302         var r = [];
5303         for(var i = 0, len = c2.length; i < len; i++){
5304             if(c2[i]._qdiff != d){
5305                 r[r.length] = c2[i];
5306             }
5307         }
5308         return r;
5309     }
5310
5311     function quickId(ns, mode, root, id){
5312         if(ns == root){
5313            var d = root.ownerDocument || root;
5314            return d.getElementById(id);
5315         }
5316         ns = getNodes(ns, mode, "*");
5317         return byId(ns, null, id);
5318     }
5319
5320     return {
5321         getStyle : function(el, name){
5322             return Roo.fly(el).getStyle(name);
5323         },
5324         /**
5325          * Compiles a selector/xpath query into a reusable function. The returned function
5326          * takes one parameter "root" (optional), which is the context node from where the query should start.
5327          * @param {String} selector The selector/xpath query
5328          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5329          * @return {Function}
5330          */
5331         compile : function(path, type){
5332             type = type || "select";
5333             
5334             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5335             var q = path, mode, lq;
5336             var tk = Roo.DomQuery.matchers;
5337             var tklen = tk.length;
5338             var mm;
5339
5340             // accept leading mode switch
5341             var lmode = q.match(modeRe);
5342             if(lmode && lmode[1]){
5343                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5344                 q = q.replace(lmode[1], "");
5345             }
5346             // strip leading slashes
5347             while(path.substr(0, 1)=="/"){
5348                 path = path.substr(1);
5349             }
5350
5351             while(q && lq != q){
5352                 lq = q;
5353                 var tm = q.match(tagTokenRe);
5354                 if(type == "select"){
5355                     if(tm){
5356                         if(tm[1] == "#"){
5357                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5358                         }else{
5359                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5360                         }
5361                         q = q.replace(tm[0], "");
5362                     }else if(q.substr(0, 1) != '@'){
5363                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5364                     }
5365                 }else{
5366                     if(tm){
5367                         if(tm[1] == "#"){
5368                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5369                         }else{
5370                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5371                         }
5372                         q = q.replace(tm[0], "");
5373                     }
5374                 }
5375                 while(!(mm = q.match(modeRe))){
5376                     var matched = false;
5377                     for(var j = 0; j < tklen; j++){
5378                         var t = tk[j];
5379                         var m = q.match(t.re);
5380                         if(m){
5381                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5382                                                     return m[i];
5383                                                 });
5384                             q = q.replace(m[0], "");
5385                             matched = true;
5386                             break;
5387                         }
5388                     }
5389                     // prevent infinite loop on bad selector
5390                     if(!matched){
5391                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5392                     }
5393                 }
5394                 if(mm[1]){
5395                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5396                     q = q.replace(mm[1], "");
5397                 }
5398             }
5399             fn[fn.length] = "return nodup(n);\n}";
5400             
5401              /** 
5402               * list of variables that need from compression as they are used by eval.
5403              *  eval:var:batch 
5404              *  eval:var:nodup
5405              *  eval:var:byTag
5406              *  eval:var:ById
5407              *  eval:var:getNodes
5408              *  eval:var:quickId
5409              *  eval:var:mode
5410              *  eval:var:root
5411              *  eval:var:n
5412              *  eval:var:byClassName
5413              *  eval:var:byPseudo
5414              *  eval:var:byAttribute
5415              *  eval:var:attrValue
5416              * 
5417              **/ 
5418             eval(fn.join(""));
5419             return f;
5420         },
5421
5422         /**
5423          * Selects a group of elements.
5424          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5425          * @param {Node} root (optional) The start of the query (defaults to document).
5426          * @return {Array}
5427          */
5428         select : function(path, root, type){
5429             if(!root || root == document){
5430                 root = document;
5431             }
5432             if(typeof root == "string"){
5433                 root = document.getElementById(root);
5434             }
5435             var paths = path.split(",");
5436             var results = [];
5437             for(var i = 0, len = paths.length; i < len; i++){
5438                 var p = paths[i].replace(trimRe, "");
5439                 if(!cache[p]){
5440                     cache[p] = Roo.DomQuery.compile(p);
5441                     if(!cache[p]){
5442                         throw p + " is not a valid selector";
5443                     }
5444                 }
5445                 var result = cache[p](root);
5446                 if(result && result != document){
5447                     results = results.concat(result);
5448                 }
5449             }
5450             if(paths.length > 1){
5451                 return nodup(results);
5452             }
5453             return results;
5454         },
5455
5456         /**
5457          * Selects a single element.
5458          * @param {String} selector The selector/xpath query
5459          * @param {Node} root (optional) The start of the query (defaults to document).
5460          * @return {Element}
5461          */
5462         selectNode : function(path, root){
5463             return Roo.DomQuery.select(path, root)[0];
5464         },
5465
5466         /**
5467          * Selects the value of a node, optionally replacing null with the defaultValue.
5468          * @param {String} selector The selector/xpath query
5469          * @param {Node} root (optional) The start of the query (defaults to document).
5470          * @param {String} defaultValue
5471          */
5472         selectValue : function(path, root, defaultValue){
5473             path = path.replace(trimRe, "");
5474             if(!valueCache[path]){
5475                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5476             }
5477             var n = valueCache[path](root);
5478             n = n[0] ? n[0] : n;
5479             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5480             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5481         },
5482
5483         /**
5484          * Selects the value of a node, parsing integers and floats.
5485          * @param {String} selector The selector/xpath query
5486          * @param {Node} root (optional) The start of the query (defaults to document).
5487          * @param {Number} defaultValue
5488          * @return {Number}
5489          */
5490         selectNumber : function(path, root, defaultValue){
5491             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5492             return parseFloat(v);
5493         },
5494
5495         /**
5496          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5497          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5498          * @param {String} selector The simple selector to test
5499          * @return {Boolean}
5500          */
5501         is : function(el, ss){
5502             if(typeof el == "string"){
5503                 el = document.getElementById(el);
5504             }
5505             var isArray = (el instanceof Array);
5506             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5507             return isArray ? (result.length == el.length) : (result.length > 0);
5508         },
5509
5510         /**
5511          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5512          * @param {Array} el An array of elements to filter
5513          * @param {String} selector The simple selector to test
5514          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5515          * the selector instead of the ones that match
5516          * @return {Array}
5517          */
5518         filter : function(els, ss, nonMatches){
5519             ss = ss.replace(trimRe, "");
5520             if(!simpleCache[ss]){
5521                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5522             }
5523             var result = simpleCache[ss](els);
5524             return nonMatches ? quickDiff(result, els) : result;
5525         },
5526
5527         /**
5528          * Collection of matching regular expressions and code snippets.
5529          */
5530         matchers : [{
5531                 re: /^\.([\w-]+)/,
5532                 select: 'n = byClassName(n, null, " {1} ");'
5533             }, {
5534                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5535                 select: 'n = byPseudo(n, "{1}", "{2}");'
5536             },{
5537                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5538                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5539             }, {
5540                 re: /^#([\w-]+)/,
5541                 select: 'n = byId(n, null, "{1}");'
5542             },{
5543                 re: /^@([\w-]+)/,
5544                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5545             }
5546         ],
5547
5548         /**
5549          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5550          * 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;.
5551          */
5552         operators : {
5553             "=" : function(a, v){
5554                 return a == v;
5555             },
5556             "!=" : function(a, v){
5557                 return a != v;
5558             },
5559             "^=" : function(a, v){
5560                 return a && a.substr(0, v.length) == v;
5561             },
5562             "$=" : function(a, v){
5563                 return a && a.substr(a.length-v.length) == v;
5564             },
5565             "*=" : function(a, v){
5566                 return a && a.indexOf(v) !== -1;
5567             },
5568             "%=" : function(a, v){
5569                 return (a % v) == 0;
5570             },
5571             "|=" : function(a, v){
5572                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5573             },
5574             "~=" : function(a, v){
5575                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5576             }
5577         },
5578
5579         /**
5580          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5581          * and the argument (if any) supplied in the selector.
5582          */
5583         pseudos : {
5584             "first-child" : function(c){
5585                 var r = [], ri = -1, n;
5586                 for(var i = 0, ci; ci = n = c[i]; i++){
5587                     while((n = n.previousSibling) && n.nodeType != 1);
5588                     if(!n){
5589                         r[++ri] = ci;
5590                     }
5591                 }
5592                 return r;
5593             },
5594
5595             "last-child" : function(c){
5596                 var r = [], ri = -1, n;
5597                 for(var i = 0, ci; ci = n = c[i]; i++){
5598                     while((n = n.nextSibling) && n.nodeType != 1);
5599                     if(!n){
5600                         r[++ri] = ci;
5601                     }
5602                 }
5603                 return r;
5604             },
5605
5606             "nth-child" : function(c, a) {
5607                 var r = [], ri = -1;
5608                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5609                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5610                 for(var i = 0, n; n = c[i]; i++){
5611                     var pn = n.parentNode;
5612                     if (batch != pn._batch) {
5613                         var j = 0;
5614                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5615                             if(cn.nodeType == 1){
5616                                cn.nodeIndex = ++j;
5617                             }
5618                         }
5619                         pn._batch = batch;
5620                     }
5621                     if (f == 1) {
5622                         if (l == 0 || n.nodeIndex == l){
5623                             r[++ri] = n;
5624                         }
5625                     } else if ((n.nodeIndex + l) % f == 0){
5626                         r[++ri] = n;
5627                     }
5628                 }
5629
5630                 return r;
5631             },
5632
5633             "only-child" : function(c){
5634                 var r = [], ri = -1;;
5635                 for(var i = 0, ci; ci = c[i]; i++){
5636                     if(!prev(ci) && !next(ci)){
5637                         r[++ri] = ci;
5638                     }
5639                 }
5640                 return r;
5641             },
5642
5643             "empty" : function(c){
5644                 var r = [], ri = -1;
5645                 for(var i = 0, ci; ci = c[i]; i++){
5646                     var cns = ci.childNodes, j = 0, cn, empty = true;
5647                     while(cn = cns[j]){
5648                         ++j;
5649                         if(cn.nodeType == 1 || cn.nodeType == 3){
5650                             empty = false;
5651                             break;
5652                         }
5653                     }
5654                     if(empty){
5655                         r[++ri] = ci;
5656                     }
5657                 }
5658                 return r;
5659             },
5660
5661             "contains" : function(c, v){
5662                 var r = [], ri = -1;
5663                 for(var i = 0, ci; ci = c[i]; i++){
5664                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5665                         r[++ri] = ci;
5666                     }
5667                 }
5668                 return r;
5669             },
5670
5671             "nodeValue" : function(c, v){
5672                 var r = [], ri = -1;
5673                 for(var i = 0, ci; ci = c[i]; i++){
5674                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5675                         r[++ri] = ci;
5676                     }
5677                 }
5678                 return r;
5679             },
5680
5681             "checked" : function(c){
5682                 var r = [], ri = -1;
5683                 for(var i = 0, ci; ci = c[i]; i++){
5684                     if(ci.checked == true){
5685                         r[++ri] = ci;
5686                     }
5687                 }
5688                 return r;
5689             },
5690
5691             "not" : function(c, ss){
5692                 return Roo.DomQuery.filter(c, ss, true);
5693             },
5694
5695             "odd" : function(c){
5696                 return this["nth-child"](c, "odd");
5697             },
5698
5699             "even" : function(c){
5700                 return this["nth-child"](c, "even");
5701             },
5702
5703             "nth" : function(c, a){
5704                 return c[a-1] || [];
5705             },
5706
5707             "first" : function(c){
5708                 return c[0] || [];
5709             },
5710
5711             "last" : function(c){
5712                 return c[c.length-1] || [];
5713             },
5714
5715             "has" : function(c, ss){
5716                 var s = Roo.DomQuery.select;
5717                 var r = [], ri = -1;
5718                 for(var i = 0, ci; ci = c[i]; i++){
5719                     if(s(ss, ci).length > 0){
5720                         r[++ri] = ci;
5721                     }
5722                 }
5723                 return r;
5724             },
5725
5726             "next" : function(c, ss){
5727                 var is = Roo.DomQuery.is;
5728                 var r = [], ri = -1;
5729                 for(var i = 0, ci; ci = c[i]; i++){
5730                     var n = next(ci);
5731                     if(n && is(n, ss)){
5732                         r[++ri] = ci;
5733                     }
5734                 }
5735                 return r;
5736             },
5737
5738             "prev" : function(c, ss){
5739                 var is = Roo.DomQuery.is;
5740                 var r = [], ri = -1;
5741                 for(var i = 0, ci; ci = c[i]; i++){
5742                     var n = prev(ci);
5743                     if(n && is(n, ss)){
5744                         r[++ri] = ci;
5745                     }
5746                 }
5747                 return r;
5748             }
5749         }
5750     };
5751 }();
5752
5753 /**
5754  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5755  * @param {String} path The selector/xpath query
5756  * @param {Node} root (optional) The start of the query (defaults to document).
5757  * @return {Array}
5758  * @member Roo
5759  * @method query
5760  */
5761 Roo.query = Roo.DomQuery.select;
5762 /*
5763  * Based on:
5764  * Ext JS Library 1.1.1
5765  * Copyright(c) 2006-2007, Ext JS, LLC.
5766  *
5767  * Originally Released Under LGPL - original licence link has changed is not relivant.
5768  *
5769  * Fork - LGPL
5770  * <script type="text/javascript">
5771  */
5772
5773 /**
5774  * @class Roo.util.Observable
5775  * Base class that provides a common interface for publishing events. Subclasses are expected to
5776  * to have a property "events" with all the events defined.<br>
5777  * For example:
5778  * <pre><code>
5779  Employee = function(name){
5780     this.name = name;
5781     this.addEvents({
5782         "fired" : true,
5783         "quit" : true
5784     });
5785  }
5786  Roo.extend(Employee, Roo.util.Observable);
5787 </code></pre>
5788  * @param {Object} config properties to use (incuding events / listeners)
5789  */
5790
5791 Roo.util.Observable = function(cfg){
5792     
5793     cfg = cfg|| {};
5794     this.addEvents(cfg.events || {});
5795     if (cfg.events) {
5796         delete cfg.events; // make sure
5797     }
5798      
5799     Roo.apply(this, cfg);
5800     
5801     if(this.listeners){
5802         this.on(this.listeners);
5803         delete this.listeners;
5804     }
5805 };
5806 Roo.util.Observable.prototype = {
5807     /** 
5808  * @cfg {Object} listeners  list of events and functions to call for this object, 
5809  * For example :
5810  * <pre><code>
5811     listeners :  { 
5812        'click' : function(e) {
5813            ..... 
5814         } ,
5815         .... 
5816     } 
5817   </code></pre>
5818  */
5819     
5820     
5821     /**
5822      * Fires the specified event with the passed parameters (minus the event name).
5823      * @param {String} eventName
5824      * @param {Object...} args Variable number of parameters are passed to handlers
5825      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5826      */
5827     fireEvent : function(){
5828         var ce = this.events[arguments[0].toLowerCase()];
5829         if(typeof ce == "object"){
5830             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5831         }else{
5832             return true;
5833         }
5834     },
5835
5836     // private
5837     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5838
5839     /**
5840      * Appends an event handler to this component
5841      * @param {String}   eventName The type of event to listen for
5842      * @param {Function} handler The method the event invokes
5843      * @param {Object}   scope (optional) The scope in which to execute the handler
5844      * function. The handler function's "this" context.
5845      * @param {Object}   options (optional) An object containing handler configuration
5846      * properties. This may contain any of the following properties:<ul>
5847      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5848      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5849      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5850      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5851      * by the specified number of milliseconds. If the event fires again within that time, the original
5852      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5853      * </ul><br>
5854      * <p>
5855      * <b>Combining Options</b><br>
5856      * Using the options argument, it is possible to combine different types of listeners:<br>
5857      * <br>
5858      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5859                 <pre><code>
5860                 el.on('click', this.onClick, this, {
5861                         single: true,
5862                 delay: 100,
5863                 forumId: 4
5864                 });
5865                 </code></pre>
5866      * <p>
5867      * <b>Attaching multiple handlers in 1 call</b><br>
5868      * The method also allows for a single argument to be passed which is a config object containing properties
5869      * which specify multiple handlers.
5870      * <pre><code>
5871                 el.on({
5872                         'click': {
5873                         fn: this.onClick,
5874                         scope: this,
5875                         delay: 100
5876                 }, 
5877                 'mouseover': {
5878                         fn: this.onMouseOver,
5879                         scope: this
5880                 },
5881                 'mouseout': {
5882                         fn: this.onMouseOut,
5883                         scope: this
5884                 }
5885                 });
5886                 </code></pre>
5887      * <p>
5888      * Or a shorthand syntax which passes the same scope object to all handlers:
5889         <pre><code>
5890                 el.on({
5891                         'click': this.onClick,
5892                 'mouseover': this.onMouseOver,
5893                 'mouseout': this.onMouseOut,
5894                 scope: this
5895                 });
5896                 </code></pre>
5897      */
5898     addListener : function(eventName, fn, scope, o){
5899         if(typeof eventName == "object"){
5900             o = eventName;
5901             for(var e in o){
5902                 if(this.filterOptRe.test(e)){
5903                     continue;
5904                 }
5905                 if(typeof o[e] == "function"){
5906                     // shared options
5907                     this.addListener(e, o[e], o.scope,  o);
5908                 }else{
5909                     // individual options
5910                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5911                 }
5912             }
5913             return;
5914         }
5915         o = (!o || typeof o == "boolean") ? {} : o;
5916         eventName = eventName.toLowerCase();
5917         var ce = this.events[eventName] || true;
5918         if(typeof ce == "boolean"){
5919             ce = new Roo.util.Event(this, eventName);
5920             this.events[eventName] = ce;
5921         }
5922         ce.addListener(fn, scope, o);
5923     },
5924
5925     /**
5926      * Removes a listener
5927      * @param {String}   eventName     The type of event to listen for
5928      * @param {Function} handler        The handler to remove
5929      * @param {Object}   scope  (optional) The scope (this object) for the handler
5930      */
5931     removeListener : function(eventName, fn, scope){
5932         var ce = this.events[eventName.toLowerCase()];
5933         if(typeof ce == "object"){
5934             ce.removeListener(fn, scope);
5935         }
5936     },
5937
5938     /**
5939      * Removes all listeners for this object
5940      */
5941     purgeListeners : function(){
5942         for(var evt in this.events){
5943             if(typeof this.events[evt] == "object"){
5944                  this.events[evt].clearListeners();
5945             }
5946         }
5947     },
5948
5949     relayEvents : function(o, events){
5950         var createHandler = function(ename){
5951             return function(){
5952                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5953             };
5954         };
5955         for(var i = 0, len = events.length; i < len; i++){
5956             var ename = events[i];
5957             if(!this.events[ename]){ this.events[ename] = true; };
5958             o.on(ename, createHandler(ename), this);
5959         }
5960     },
5961
5962     /**
5963      * Used to define events on this Observable
5964      * @param {Object} object The object with the events defined
5965      */
5966     addEvents : function(o){
5967         if(!this.events){
5968             this.events = {};
5969         }
5970         Roo.applyIf(this.events, o);
5971     },
5972
5973     /**
5974      * Checks to see if this object has any listeners for a specified event
5975      * @param {String} eventName The name of the event to check for
5976      * @return {Boolean} True if the event is being listened for, else false
5977      */
5978     hasListener : function(eventName){
5979         var e = this.events[eventName];
5980         return typeof e == "object" && e.listeners.length > 0;
5981     }
5982 };
5983 /**
5984  * Appends an event handler to this element (shorthand for addListener)
5985  * @param {String}   eventName     The type of event to listen for
5986  * @param {Function} handler        The method the event invokes
5987  * @param {Object}   scope (optional) The scope in which to execute the handler
5988  * function. The handler function's "this" context.
5989  * @param {Object}   options  (optional)
5990  * @method
5991  */
5992 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5993 /**
5994  * Removes a listener (shorthand for removeListener)
5995  * @param {String}   eventName     The type of event to listen for
5996  * @param {Function} handler        The handler to remove
5997  * @param {Object}   scope  (optional) The scope (this object) for the handler
5998  * @method
5999  */
6000 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6001
6002 /**
6003  * Starts capture on the specified Observable. All events will be passed
6004  * to the supplied function with the event name + standard signature of the event
6005  * <b>before</b> the event is fired. If the supplied function returns false,
6006  * the event will not fire.
6007  * @param {Observable} o The Observable to capture
6008  * @param {Function} fn The function to call
6009  * @param {Object} scope (optional) The scope (this object) for the fn
6010  * @static
6011  */
6012 Roo.util.Observable.capture = function(o, fn, scope){
6013     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6014 };
6015
6016 /**
6017  * Removes <b>all</b> added captures from the Observable.
6018  * @param {Observable} o The Observable to release
6019  * @static
6020  */
6021 Roo.util.Observable.releaseCapture = function(o){
6022     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6023 };
6024
6025 (function(){
6026
6027     var createBuffered = function(h, o, scope){
6028         var task = new Roo.util.DelayedTask();
6029         return function(){
6030             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6031         };
6032     };
6033
6034     var createSingle = function(h, e, fn, scope){
6035         return function(){
6036             e.removeListener(fn, scope);
6037             return h.apply(scope, arguments);
6038         };
6039     };
6040
6041     var createDelayed = function(h, o, scope){
6042         return function(){
6043             var args = Array.prototype.slice.call(arguments, 0);
6044             setTimeout(function(){
6045                 h.apply(scope, args);
6046             }, o.delay || 10);
6047         };
6048     };
6049
6050     Roo.util.Event = function(obj, name){
6051         this.name = name;
6052         this.obj = obj;
6053         this.listeners = [];
6054     };
6055
6056     Roo.util.Event.prototype = {
6057         addListener : function(fn, scope, options){
6058             var o = options || {};
6059             scope = scope || this.obj;
6060             if(!this.isListening(fn, scope)){
6061                 var l = {fn: fn, scope: scope, options: o};
6062                 var h = fn;
6063                 if(o.delay){
6064                     h = createDelayed(h, o, scope);
6065                 }
6066                 if(o.single){
6067                     h = createSingle(h, this, fn, scope);
6068                 }
6069                 if(o.buffer){
6070                     h = createBuffered(h, o, scope);
6071                 }
6072                 l.fireFn = h;
6073                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6074                     this.listeners.push(l);
6075                 }else{
6076                     this.listeners = this.listeners.slice(0);
6077                     this.listeners.push(l);
6078                 }
6079             }
6080         },
6081
6082         findListener : function(fn, scope){
6083             scope = scope || this.obj;
6084             var ls = this.listeners;
6085             for(var i = 0, len = ls.length; i < len; i++){
6086                 var l = ls[i];
6087                 if(l.fn == fn && l.scope == scope){
6088                     return i;
6089                 }
6090             }
6091             return -1;
6092         },
6093
6094         isListening : function(fn, scope){
6095             return this.findListener(fn, scope) != -1;
6096         },
6097
6098         removeListener : function(fn, scope){
6099             var index;
6100             if((index = this.findListener(fn, scope)) != -1){
6101                 if(!this.firing){
6102                     this.listeners.splice(index, 1);
6103                 }else{
6104                     this.listeners = this.listeners.slice(0);
6105                     this.listeners.splice(index, 1);
6106                 }
6107                 return true;
6108             }
6109             return false;
6110         },
6111
6112         clearListeners : function(){
6113             this.listeners = [];
6114         },
6115
6116         fire : function(){
6117             var ls = this.listeners, scope, len = ls.length;
6118             if(len > 0){
6119                 this.firing = true;
6120                 var args = Array.prototype.slice.call(arguments, 0);
6121                 for(var i = 0; i < len; i++){
6122                     var l = ls[i];
6123                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6124                         this.firing = false;
6125                         return false;
6126                     }
6127                 }
6128                 this.firing = false;
6129             }
6130             return true;
6131         }
6132     };
6133 })();/*
6134  * Based on:
6135  * Ext JS Library 1.1.1
6136  * Copyright(c) 2006-2007, Ext JS, LLC.
6137  *
6138  * Originally Released Under LGPL - original licence link has changed is not relivant.
6139  *
6140  * Fork - LGPL
6141  * <script type="text/javascript">
6142  */
6143
6144 /**
6145  * @class Roo.EventManager
6146  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6147  * several useful events directly.
6148  * See {@link Roo.EventObject} for more details on normalized event objects.
6149  * @singleton
6150  */
6151 Roo.EventManager = function(){
6152     var docReadyEvent, docReadyProcId, docReadyState = false;
6153     var resizeEvent, resizeTask, textEvent, textSize;
6154     var E = Roo.lib.Event;
6155     var D = Roo.lib.Dom;
6156
6157
6158     var fireDocReady = function(){
6159         if(!docReadyState){
6160             docReadyState = true;
6161             Roo.isReady = true;
6162             if(docReadyProcId){
6163                 clearInterval(docReadyProcId);
6164             }
6165             if(Roo.isGecko || Roo.isOpera) {
6166                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6167             }
6168             if(Roo.isIE){
6169                 var defer = document.getElementById("ie-deferred-loader");
6170                 if(defer){
6171                     defer.onreadystatechange = null;
6172                     defer.parentNode.removeChild(defer);
6173                 }
6174             }
6175             if(docReadyEvent){
6176                 docReadyEvent.fire();
6177                 docReadyEvent.clearListeners();
6178             }
6179         }
6180     };
6181     
6182     var initDocReady = function(){
6183         docReadyEvent = new Roo.util.Event();
6184         if(Roo.isGecko || Roo.isOpera) {
6185             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6186         }else if(Roo.isIE){
6187             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6188             var defer = document.getElementById("ie-deferred-loader");
6189             defer.onreadystatechange = function(){
6190                 if(this.readyState == "complete"){
6191                     fireDocReady();
6192                 }
6193             };
6194         }else if(Roo.isSafari){ 
6195             docReadyProcId = setInterval(function(){
6196                 var rs = document.readyState;
6197                 if(rs == "complete") {
6198                     fireDocReady();     
6199                  }
6200             }, 10);
6201         }
6202         // no matter what, make sure it fires on load
6203         E.on(window, "load", fireDocReady);
6204     };
6205
6206     var createBuffered = function(h, o){
6207         var task = new Roo.util.DelayedTask(h);
6208         return function(e){
6209             // create new event object impl so new events don't wipe out properties
6210             e = new Roo.EventObjectImpl(e);
6211             task.delay(o.buffer, h, null, [e]);
6212         };
6213     };
6214
6215     var createSingle = function(h, el, ename, fn){
6216         return function(e){
6217             Roo.EventManager.removeListener(el, ename, fn);
6218             h(e);
6219         };
6220     };
6221
6222     var createDelayed = function(h, o){
6223         return function(e){
6224             // create new event object impl so new events don't wipe out properties
6225             e = new Roo.EventObjectImpl(e);
6226             setTimeout(function(){
6227                 h(e);
6228             }, o.delay || 10);
6229         };
6230     };
6231
6232     var listen = function(element, ename, opt, fn, scope){
6233         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6234         fn = fn || o.fn; scope = scope || o.scope;
6235         var el = Roo.getDom(element);
6236         if(!el){
6237             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6238         }
6239         var h = function(e){
6240             e = Roo.EventObject.setEvent(e);
6241             var t;
6242             if(o.delegate){
6243                 t = e.getTarget(o.delegate, el);
6244                 if(!t){
6245                     return;
6246                 }
6247             }else{
6248                 t = e.target;
6249             }
6250             if(o.stopEvent === true){
6251                 e.stopEvent();
6252             }
6253             if(o.preventDefault === true){
6254                e.preventDefault();
6255             }
6256             if(o.stopPropagation === true){
6257                 e.stopPropagation();
6258             }
6259
6260             if(o.normalized === false){
6261                 e = e.browserEvent;
6262             }
6263
6264             fn.call(scope || el, e, t, o);
6265         };
6266         if(o.delay){
6267             h = createDelayed(h, o);
6268         }
6269         if(o.single){
6270             h = createSingle(h, el, ename, fn);
6271         }
6272         if(o.buffer){
6273             h = createBuffered(h, o);
6274         }
6275         fn._handlers = fn._handlers || [];
6276         fn._handlers.push([Roo.id(el), ename, h]);
6277
6278         E.on(el, ename, h);
6279         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6280             el.addEventListener("DOMMouseScroll", h, false);
6281             E.on(window, 'unload', function(){
6282                 el.removeEventListener("DOMMouseScroll", h, false);
6283             });
6284         }
6285         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6286             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6287         }
6288         return h;
6289     };
6290
6291     var stopListening = function(el, ename, fn){
6292         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6293         if(hds){
6294             for(var i = 0, len = hds.length; i < len; i++){
6295                 var h = hds[i];
6296                 if(h[0] == id && h[1] == ename){
6297                     hd = h[2];
6298                     hds.splice(i, 1);
6299                     break;
6300                 }
6301             }
6302         }
6303         E.un(el, ename, hd);
6304         el = Roo.getDom(el);
6305         if(ename == "mousewheel" && el.addEventListener){
6306             el.removeEventListener("DOMMouseScroll", hd, false);
6307         }
6308         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6309             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6310         }
6311     };
6312
6313     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6314     
6315     var pub = {
6316         
6317         
6318         /** 
6319          * Fix for doc tools
6320          * @scope Roo.EventManager
6321          */
6322         
6323         
6324         /** 
6325          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6326          * object with a Roo.EventObject
6327          * @param {Function} fn        The method the event invokes
6328          * @param {Object}   scope    An object that becomes the scope of the handler
6329          * @param {boolean}  override If true, the obj passed in becomes
6330          *                             the execution scope of the listener
6331          * @return {Function} The wrapped function
6332          * @deprecated
6333          */
6334         wrap : function(fn, scope, override){
6335             return function(e){
6336                 Roo.EventObject.setEvent(e);
6337                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6338             };
6339         },
6340         
6341         /**
6342      * Appends an event handler to an element (shorthand for addListener)
6343      * @param {String/HTMLElement}   element        The html element or id to assign the
6344      * @param {String}   eventName The type of event to listen for
6345      * @param {Function} handler The method the event invokes
6346      * @param {Object}   scope (optional) The scope in which to execute the handler
6347      * function. The handler function's "this" context.
6348      * @param {Object}   options (optional) An object containing handler configuration
6349      * properties. This may contain any of the following properties:<ul>
6350      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6351      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6352      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6353      * <li>preventDefault {Boolean} True to prevent the default action</li>
6354      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6355      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6356      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6357      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6358      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6359      * by the specified number of milliseconds. If the event fires again within that time, the original
6360      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6361      * </ul><br>
6362      * <p>
6363      * <b>Combining Options</b><br>
6364      * Using the options argument, it is possible to combine different types of listeners:<br>
6365      * <br>
6366      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6367      * Code:<pre><code>
6368 el.on('click', this.onClick, this, {
6369     single: true,
6370     delay: 100,
6371     stopEvent : true,
6372     forumId: 4
6373 });</code></pre>
6374      * <p>
6375      * <b>Attaching multiple handlers in 1 call</b><br>
6376       * The method also allows for a single argument to be passed which is a config object containing properties
6377      * which specify multiple handlers.
6378      * <p>
6379      * Code:<pre><code>
6380 el.on({
6381     'click' : {
6382         fn: this.onClick
6383         scope: this,
6384         delay: 100
6385     },
6386     'mouseover' : {
6387         fn: this.onMouseOver
6388         scope: this
6389     },
6390     'mouseout' : {
6391         fn: this.onMouseOut
6392         scope: this
6393     }
6394 });</code></pre>
6395      * <p>
6396      * Or a shorthand syntax:<br>
6397      * Code:<pre><code>
6398 el.on({
6399     'click' : this.onClick,
6400     'mouseover' : this.onMouseOver,
6401     'mouseout' : this.onMouseOut
6402     scope: this
6403 });</code></pre>
6404      */
6405         addListener : function(element, eventName, fn, scope, options){
6406             if(typeof eventName == "object"){
6407                 var o = eventName;
6408                 for(var e in o){
6409                     if(propRe.test(e)){
6410                         continue;
6411                     }
6412                     if(typeof o[e] == "function"){
6413                         // shared options
6414                         listen(element, e, o, o[e], o.scope);
6415                     }else{
6416                         // individual options
6417                         listen(element, e, o[e]);
6418                     }
6419                 }
6420                 return;
6421             }
6422             return listen(element, eventName, options, fn, scope);
6423         },
6424         
6425         /**
6426          * Removes an event handler
6427          *
6428          * @param {String/HTMLElement}   element        The id or html element to remove the 
6429          *                             event from
6430          * @param {String}   eventName     The type of event
6431          * @param {Function} fn
6432          * @return {Boolean} True if a listener was actually removed
6433          */
6434         removeListener : function(element, eventName, fn){
6435             return stopListening(element, eventName, fn);
6436         },
6437         
6438         /**
6439          * Fires when the document is ready (before onload and before images are loaded). Can be 
6440          * accessed shorthanded Roo.onReady().
6441          * @param {Function} fn        The method the event invokes
6442          * @param {Object}   scope    An  object that becomes the scope of the handler
6443          * @param {boolean}  options
6444          */
6445         onDocumentReady : function(fn, scope, options){
6446             if(docReadyState){ // if it already fired
6447                 docReadyEvent.addListener(fn, scope, options);
6448                 docReadyEvent.fire();
6449                 docReadyEvent.clearListeners();
6450                 return;
6451             }
6452             if(!docReadyEvent){
6453                 initDocReady();
6454             }
6455             docReadyEvent.addListener(fn, scope, options);
6456         },
6457         
6458         /**
6459          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6460          * @param {Function} fn        The method the event invokes
6461          * @param {Object}   scope    An object that becomes the scope of the handler
6462          * @param {boolean}  options
6463          */
6464         onWindowResize : function(fn, scope, options){
6465             if(!resizeEvent){
6466                 resizeEvent = new Roo.util.Event();
6467                 resizeTask = new Roo.util.DelayedTask(function(){
6468                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6469                 });
6470                 E.on(window, "resize", function(){
6471                     if(Roo.isIE){
6472                         resizeTask.delay(50);
6473                     }else{
6474                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6475                     }
6476                 });
6477             }
6478             resizeEvent.addListener(fn, scope, options);
6479         },
6480
6481         /**
6482          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6483          * @param {Function} fn        The method the event invokes
6484          * @param {Object}   scope    An object that becomes the scope of the handler
6485          * @param {boolean}  options
6486          */
6487         onTextResize : function(fn, scope, options){
6488             if(!textEvent){
6489                 textEvent = new Roo.util.Event();
6490                 var textEl = new Roo.Element(document.createElement('div'));
6491                 textEl.dom.className = 'x-text-resize';
6492                 textEl.dom.innerHTML = 'X';
6493                 textEl.appendTo(document.body);
6494                 textSize = textEl.dom.offsetHeight;
6495                 setInterval(function(){
6496                     if(textEl.dom.offsetHeight != textSize){
6497                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6498                     }
6499                 }, this.textResizeInterval);
6500             }
6501             textEvent.addListener(fn, scope, options);
6502         },
6503
6504         /**
6505          * Removes the passed window resize listener.
6506          * @param {Function} fn        The method the event invokes
6507          * @param {Object}   scope    The scope of handler
6508          */
6509         removeResizeListener : function(fn, scope){
6510             if(resizeEvent){
6511                 resizeEvent.removeListener(fn, scope);
6512             }
6513         },
6514
6515         // private
6516         fireResize : function(){
6517             if(resizeEvent){
6518                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6519             }   
6520         },
6521         /**
6522          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6523          */
6524         ieDeferSrc : false,
6525         /**
6526          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6527          */
6528         textResizeInterval : 50
6529     };
6530     
6531     /**
6532      * Fix for doc tools
6533      * @scopeAlias pub=Roo.EventManager
6534      */
6535     
6536      /**
6537      * Appends an event handler to an element (shorthand for addListener)
6538      * @param {String/HTMLElement}   element        The html element or id to assign the
6539      * @param {String}   eventName The type of event to listen for
6540      * @param {Function} handler The method the event invokes
6541      * @param {Object}   scope (optional) The scope in which to execute the handler
6542      * function. The handler function's "this" context.
6543      * @param {Object}   options (optional) An object containing handler configuration
6544      * properties. This may contain any of the following properties:<ul>
6545      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6546      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6547      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6548      * <li>preventDefault {Boolean} True to prevent the default action</li>
6549      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6550      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6551      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6552      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6553      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6554      * by the specified number of milliseconds. If the event fires again within that time, the original
6555      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6556      * </ul><br>
6557      * <p>
6558      * <b>Combining Options</b><br>
6559      * Using the options argument, it is possible to combine different types of listeners:<br>
6560      * <br>
6561      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6562      * Code:<pre><code>
6563 el.on('click', this.onClick, this, {
6564     single: true,
6565     delay: 100,
6566     stopEvent : true,
6567     forumId: 4
6568 });</code></pre>
6569      * <p>
6570      * <b>Attaching multiple handlers in 1 call</b><br>
6571       * The method also allows for a single argument to be passed which is a config object containing properties
6572      * which specify multiple handlers.
6573      * <p>
6574      * Code:<pre><code>
6575 el.on({
6576     'click' : {
6577         fn: this.onClick
6578         scope: this,
6579         delay: 100
6580     },
6581     'mouseover' : {
6582         fn: this.onMouseOver
6583         scope: this
6584     },
6585     'mouseout' : {
6586         fn: this.onMouseOut
6587         scope: this
6588     }
6589 });</code></pre>
6590      * <p>
6591      * Or a shorthand syntax:<br>
6592      * Code:<pre><code>
6593 el.on({
6594     'click' : this.onClick,
6595     'mouseover' : this.onMouseOver,
6596     'mouseout' : this.onMouseOut
6597     scope: this
6598 });</code></pre>
6599      */
6600     pub.on = pub.addListener;
6601     pub.un = pub.removeListener;
6602
6603     pub.stoppedMouseDownEvent = new Roo.util.Event();
6604     return pub;
6605 }();
6606 /**
6607   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6608   * @param {Function} fn        The method the event invokes
6609   * @param {Object}   scope    An  object that becomes the scope of the handler
6610   * @param {boolean}  override If true, the obj passed in becomes
6611   *                             the execution scope of the listener
6612   * @member Roo
6613   * @method onReady
6614  */
6615 Roo.onReady = Roo.EventManager.onDocumentReady;
6616
6617 Roo.onReady(function(){
6618     var bd = Roo.get(document.body);
6619     if(!bd){ return; }
6620
6621     var cls = [
6622             Roo.isIE ? "roo-ie"
6623             : Roo.isGecko ? "roo-gecko"
6624             : Roo.isOpera ? "roo-opera"
6625             : Roo.isSafari ? "roo-safari" : ""];
6626
6627     if(Roo.isMac){
6628         cls.push("roo-mac");
6629     }
6630     if(Roo.isLinux){
6631         cls.push("roo-linux");
6632     }
6633     if(Roo.isBorderBox){
6634         cls.push('roo-border-box');
6635     }
6636     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6637         var p = bd.dom.parentNode;
6638         if(p){
6639             p.className += ' roo-strict';
6640         }
6641     }
6642     bd.addClass(cls.join(' '));
6643 });
6644
6645 /**
6646  * @class Roo.EventObject
6647  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6648  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6649  * Example:
6650  * <pre><code>
6651  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6652     e.preventDefault();
6653     var target = e.getTarget();
6654     ...
6655  }
6656  var myDiv = Roo.get("myDiv");
6657  myDiv.on("click", handleClick);
6658  //or
6659  Roo.EventManager.on("myDiv", 'click', handleClick);
6660  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6661  </code></pre>
6662  * @singleton
6663  */
6664 Roo.EventObject = function(){
6665     
6666     var E = Roo.lib.Event;
6667     
6668     // safari keypress events for special keys return bad keycodes
6669     var safariKeys = {
6670         63234 : 37, // left
6671         63235 : 39, // right
6672         63232 : 38, // up
6673         63233 : 40, // down
6674         63276 : 33, // page up
6675         63277 : 34, // page down
6676         63272 : 46, // delete
6677         63273 : 36, // home
6678         63275 : 35  // end
6679     };
6680
6681     // normalize button clicks
6682     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6683                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6684
6685     Roo.EventObjectImpl = function(e){
6686         if(e){
6687             this.setEvent(e.browserEvent || e);
6688         }
6689     };
6690     Roo.EventObjectImpl.prototype = {
6691         /**
6692          * Used to fix doc tools.
6693          * @scope Roo.EventObject.prototype
6694          */
6695             
6696
6697         
6698         
6699         /** The normal browser event */
6700         browserEvent : null,
6701         /** The button pressed in a mouse event */
6702         button : -1,
6703         /** True if the shift key was down during the event */
6704         shiftKey : false,
6705         /** True if the control key was down during the event */
6706         ctrlKey : false,
6707         /** True if the alt key was down during the event */
6708         altKey : false,
6709
6710         /** Key constant 
6711         * @type Number */
6712         BACKSPACE : 8,
6713         /** Key constant 
6714         * @type Number */
6715         TAB : 9,
6716         /** Key constant 
6717         * @type Number */
6718         RETURN : 13,
6719         /** Key constant 
6720         * @type Number */
6721         ENTER : 13,
6722         /** Key constant 
6723         * @type Number */
6724         SHIFT : 16,
6725         /** Key constant 
6726         * @type Number */
6727         CONTROL : 17,
6728         /** Key constant 
6729         * @type Number */
6730         ESC : 27,
6731         /** Key constant 
6732         * @type Number */
6733         SPACE : 32,
6734         /** Key constant 
6735         * @type Number */
6736         PAGEUP : 33,
6737         /** Key constant 
6738         * @type Number */
6739         PAGEDOWN : 34,
6740         /** Key constant 
6741         * @type Number */
6742         END : 35,
6743         /** Key constant 
6744         * @type Number */
6745         HOME : 36,
6746         /** Key constant 
6747         * @type Number */
6748         LEFT : 37,
6749         /** Key constant 
6750         * @type Number */
6751         UP : 38,
6752         /** Key constant 
6753         * @type Number */
6754         RIGHT : 39,
6755         /** Key constant 
6756         * @type Number */
6757         DOWN : 40,
6758         /** Key constant 
6759         * @type Number */
6760         DELETE : 46,
6761         /** Key constant 
6762         * @type Number */
6763         F5 : 116,
6764
6765            /** @private */
6766         setEvent : function(e){
6767             if(e == this || (e && e.browserEvent)){ // already wrapped
6768                 return e;
6769             }
6770             this.browserEvent = e;
6771             if(e){
6772                 // normalize buttons
6773                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6774                 if(e.type == 'click' && this.button == -1){
6775                     this.button = 0;
6776                 }
6777                 this.type = e.type;
6778                 this.shiftKey = e.shiftKey;
6779                 // mac metaKey behaves like ctrlKey
6780                 this.ctrlKey = e.ctrlKey || e.metaKey;
6781                 this.altKey = e.altKey;
6782                 // in getKey these will be normalized for the mac
6783                 this.keyCode = e.keyCode;
6784                 // keyup warnings on firefox.
6785                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6786                 // cache the target for the delayed and or buffered events
6787                 this.target = E.getTarget(e);
6788                 // same for XY
6789                 this.xy = E.getXY(e);
6790             }else{
6791                 this.button = -1;
6792                 this.shiftKey = false;
6793                 this.ctrlKey = false;
6794                 this.altKey = false;
6795                 this.keyCode = 0;
6796                 this.charCode =0;
6797                 this.target = null;
6798                 this.xy = [0, 0];
6799             }
6800             return this;
6801         },
6802
6803         /**
6804          * Stop the event (preventDefault and stopPropagation)
6805          */
6806         stopEvent : function(){
6807             if(this.browserEvent){
6808                 if(this.browserEvent.type == 'mousedown'){
6809                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6810                 }
6811                 E.stopEvent(this.browserEvent);
6812             }
6813         },
6814
6815         /**
6816          * Prevents the browsers default handling of the event.
6817          */
6818         preventDefault : function(){
6819             if(this.browserEvent){
6820                 E.preventDefault(this.browserEvent);
6821             }
6822         },
6823
6824         /** @private */
6825         isNavKeyPress : function(){
6826             var k = this.keyCode;
6827             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6828             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6829         },
6830
6831         isSpecialKey : function(){
6832             var k = this.keyCode;
6833             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6834             (k == 16) || (k == 17) ||
6835             (k >= 18 && k <= 20) ||
6836             (k >= 33 && k <= 35) ||
6837             (k >= 36 && k <= 39) ||
6838             (k >= 44 && k <= 45);
6839         },
6840         /**
6841          * Cancels bubbling of the event.
6842          */
6843         stopPropagation : function(){
6844             if(this.browserEvent){
6845                 if(this.type == 'mousedown'){
6846                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6847                 }
6848                 E.stopPropagation(this.browserEvent);
6849             }
6850         },
6851
6852         /**
6853          * Gets the key code for the event.
6854          * @return {Number}
6855          */
6856         getCharCode : function(){
6857             return this.charCode || this.keyCode;
6858         },
6859
6860         /**
6861          * Returns a normalized keyCode for the event.
6862          * @return {Number} The key code
6863          */
6864         getKey : function(){
6865             var k = this.keyCode || this.charCode;
6866             return Roo.isSafari ? (safariKeys[k] || k) : k;
6867         },
6868
6869         /**
6870          * Gets the x coordinate of the event.
6871          * @return {Number}
6872          */
6873         getPageX : function(){
6874             return this.xy[0];
6875         },
6876
6877         /**
6878          * Gets the y coordinate of the event.
6879          * @return {Number}
6880          */
6881         getPageY : function(){
6882             return this.xy[1];
6883         },
6884
6885         /**
6886          * Gets the time of the event.
6887          * @return {Number}
6888          */
6889         getTime : function(){
6890             if(this.browserEvent){
6891                 return E.getTime(this.browserEvent);
6892             }
6893             return null;
6894         },
6895
6896         /**
6897          * Gets the page coordinates of the event.
6898          * @return {Array} The xy values like [x, y]
6899          */
6900         getXY : function(){
6901             return this.xy;
6902         },
6903
6904         /**
6905          * Gets the target for the event.
6906          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6907          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6908                 search as a number or element (defaults to 10 || document.body)
6909          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6910          * @return {HTMLelement}
6911          */
6912         getTarget : function(selector, maxDepth, returnEl){
6913             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6914         },
6915         /**
6916          * Gets the related target.
6917          * @return {HTMLElement}
6918          */
6919         getRelatedTarget : function(){
6920             if(this.browserEvent){
6921                 return E.getRelatedTarget(this.browserEvent);
6922             }
6923             return null;
6924         },
6925
6926         /**
6927          * Normalizes mouse wheel delta across browsers
6928          * @return {Number} The delta
6929          */
6930         getWheelDelta : function(){
6931             var e = this.browserEvent;
6932             var delta = 0;
6933             if(e.wheelDelta){ /* IE/Opera. */
6934                 delta = e.wheelDelta/120;
6935             }else if(e.detail){ /* Mozilla case. */
6936                 delta = -e.detail/3;
6937             }
6938             return delta;
6939         },
6940
6941         /**
6942          * Returns true if the control, meta, shift or alt key was pressed during this event.
6943          * @return {Boolean}
6944          */
6945         hasModifier : function(){
6946             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6947         },
6948
6949         /**
6950          * Returns true if the target of this event equals el or is a child of el
6951          * @param {String/HTMLElement/Element} el
6952          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6953          * @return {Boolean}
6954          */
6955         within : function(el, related){
6956             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6957             return t && Roo.fly(el).contains(t);
6958         },
6959
6960         getPoint : function(){
6961             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6962         }
6963     };
6964
6965     return new Roo.EventObjectImpl();
6966 }();
6967             
6968     /*
6969  * Based on:
6970  * Ext JS Library 1.1.1
6971  * Copyright(c) 2006-2007, Ext JS, LLC.
6972  *
6973  * Originally Released Under LGPL - original licence link has changed is not relivant.
6974  *
6975  * Fork - LGPL
6976  * <script type="text/javascript">
6977  */
6978
6979  
6980 // was in Composite Element!??!?!
6981  
6982 (function(){
6983     var D = Roo.lib.Dom;
6984     var E = Roo.lib.Event;
6985     var A = Roo.lib.Anim;
6986
6987     // local style camelizing for speed
6988     var propCache = {};
6989     var camelRe = /(-[a-z])/gi;
6990     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6991     var view = document.defaultView;
6992
6993 /**
6994  * @class Roo.Element
6995  * Represents an Element in the DOM.<br><br>
6996  * Usage:<br>
6997 <pre><code>
6998 var el = Roo.get("my-div");
6999
7000 // or with getEl
7001 var el = getEl("my-div");
7002
7003 // or with a DOM element
7004 var el = Roo.get(myDivElement);
7005 </code></pre>
7006  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7007  * each call instead of constructing a new one.<br><br>
7008  * <b>Animations</b><br />
7009  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7010  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7011 <pre>
7012 Option    Default   Description
7013 --------- --------  ---------------------------------------------
7014 duration  .35       The duration of the animation in seconds
7015 easing    easeOut   The YUI easing method
7016 callback  none      A function to execute when the anim completes
7017 scope     this      The scope (this) of the callback function
7018 </pre>
7019 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7020 * manipulate the animation. Here's an example:
7021 <pre><code>
7022 var el = Roo.get("my-div");
7023
7024 // no animation
7025 el.setWidth(100);
7026
7027 // default animation
7028 el.setWidth(100, true);
7029
7030 // animation with some options set
7031 el.setWidth(100, {
7032     duration: 1,
7033     callback: this.foo,
7034     scope: this
7035 });
7036
7037 // using the "anim" property to get the Anim object
7038 var opt = {
7039     duration: 1,
7040     callback: this.foo,
7041     scope: this
7042 };
7043 el.setWidth(100, opt);
7044 ...
7045 if(opt.anim.isAnimated()){
7046     opt.anim.stop();
7047 }
7048 </code></pre>
7049 * <b> Composite (Collections of) Elements</b><br />
7050  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7051  * @constructor Create a new Element directly.
7052  * @param {String/HTMLElement} element
7053  * @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).
7054  */
7055     Roo.Element = function(element, forceNew){
7056         var dom = typeof element == "string" ?
7057                 document.getElementById(element) : element;
7058         if(!dom){ // invalid id/element
7059             return null;
7060         }
7061         var id = dom.id;
7062         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7063             return Roo.Element.cache[id];
7064         }
7065
7066         /**
7067          * The DOM element
7068          * @type HTMLElement
7069          */
7070         this.dom = dom;
7071
7072         /**
7073          * The DOM element ID
7074          * @type String
7075          */
7076         this.id = id || Roo.id(dom);
7077     };
7078
7079     var El = Roo.Element;
7080
7081     El.prototype = {
7082         /**
7083          * The element's default display mode  (defaults to "")
7084          * @type String
7085          */
7086         originalDisplay : "",
7087
7088         visibilityMode : 1,
7089         /**
7090          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7091          * @type String
7092          */
7093         defaultUnit : "px",
7094         /**
7095          * Sets the element's visibility mode. When setVisible() is called it
7096          * will use this to determine whether to set the visibility or the display property.
7097          * @param visMode Element.VISIBILITY or Element.DISPLAY
7098          * @return {Roo.Element} this
7099          */
7100         setVisibilityMode : function(visMode){
7101             this.visibilityMode = visMode;
7102             return this;
7103         },
7104         /**
7105          * Convenience method for setVisibilityMode(Element.DISPLAY)
7106          * @param {String} display (optional) What to set display to when visible
7107          * @return {Roo.Element} this
7108          */
7109         enableDisplayMode : function(display){
7110             this.setVisibilityMode(El.DISPLAY);
7111             if(typeof display != "undefined") this.originalDisplay = display;
7112             return this;
7113         },
7114
7115         /**
7116          * 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)
7117          * @param {String} selector The simple selector to test
7118          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7119                 search as a number or element (defaults to 10 || document.body)
7120          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7121          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7122          */
7123         findParent : function(simpleSelector, maxDepth, returnEl){
7124             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7125             maxDepth = maxDepth || 50;
7126             if(typeof maxDepth != "number"){
7127                 stopEl = Roo.getDom(maxDepth);
7128                 maxDepth = 10;
7129             }
7130             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7131                 if(dq.is(p, simpleSelector)){
7132                     return returnEl ? Roo.get(p) : p;
7133                 }
7134                 depth++;
7135                 p = p.parentNode;
7136             }
7137             return null;
7138         },
7139
7140
7141         /**
7142          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7143          * @param {String} selector The simple selector to test
7144          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7145                 search as a number or element (defaults to 10 || document.body)
7146          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7147          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7148          */
7149         findParentNode : function(simpleSelector, maxDepth, returnEl){
7150             var p = Roo.fly(this.dom.parentNode, '_internal');
7151             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7152         },
7153
7154         /**
7155          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7156          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7157          * @param {String} selector The simple selector to test
7158          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7159                 search as a number or element (defaults to 10 || document.body)
7160          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7161          */
7162         up : function(simpleSelector, maxDepth){
7163             return this.findParentNode(simpleSelector, maxDepth, true);
7164         },
7165
7166
7167
7168         /**
7169          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7170          * @param {String} selector The simple selector to test
7171          * @return {Boolean} True if this element matches the selector, else false
7172          */
7173         is : function(simpleSelector){
7174             return Roo.DomQuery.is(this.dom, simpleSelector);
7175         },
7176
7177         /**
7178          * Perform animation on this element.
7179          * @param {Object} args The YUI animation control args
7180          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7181          * @param {Function} onComplete (optional) Function to call when animation completes
7182          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7183          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7184          * @return {Roo.Element} this
7185          */
7186         animate : function(args, duration, onComplete, easing, animType){
7187             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7188             return this;
7189         },
7190
7191         /*
7192          * @private Internal animation call
7193          */
7194         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7195             animType = animType || 'run';
7196             opt = opt || {};
7197             var anim = Roo.lib.Anim[animType](
7198                 this.dom, args,
7199                 (opt.duration || defaultDur) || .35,
7200                 (opt.easing || defaultEase) || 'easeOut',
7201                 function(){
7202                     Roo.callback(cb, this);
7203                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7204                 },
7205                 this
7206             );
7207             opt.anim = anim;
7208             return anim;
7209         },
7210
7211         // private legacy anim prep
7212         preanim : function(a, i){
7213             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7214         },
7215
7216         /**
7217          * Removes worthless text nodes
7218          * @param {Boolean} forceReclean (optional) By default the element
7219          * keeps track if it has been cleaned already so
7220          * you can call this over and over. However, if you update the element and
7221          * need to force a reclean, you can pass true.
7222          */
7223         clean : function(forceReclean){
7224             if(this.isCleaned && forceReclean !== true){
7225                 return this;
7226             }
7227             var ns = /\S/;
7228             var d = this.dom, n = d.firstChild, ni = -1;
7229             while(n){
7230                 var nx = n.nextSibling;
7231                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7232                     d.removeChild(n);
7233                 }else{
7234                     n.nodeIndex = ++ni;
7235                 }
7236                 n = nx;
7237             }
7238             this.isCleaned = true;
7239             return this;
7240         },
7241
7242         // private
7243         calcOffsetsTo : function(el){
7244             el = Roo.get(el);
7245             var d = el.dom;
7246             var restorePos = false;
7247             if(el.getStyle('position') == 'static'){
7248                 el.position('relative');
7249                 restorePos = true;
7250             }
7251             var x = 0, y =0;
7252             var op = this.dom;
7253             while(op && op != d && op.tagName != 'HTML'){
7254                 x+= op.offsetLeft;
7255                 y+= op.offsetTop;
7256                 op = op.offsetParent;
7257             }
7258             if(restorePos){
7259                 el.position('static');
7260             }
7261             return [x, y];
7262         },
7263
7264         /**
7265          * Scrolls this element into view within the passed container.
7266          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7267          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7268          * @return {Roo.Element} this
7269          */
7270         scrollIntoView : function(container, hscroll){
7271             var c = Roo.getDom(container) || document.body;
7272             var el = this.dom;
7273
7274             var o = this.calcOffsetsTo(c),
7275                 l = o[0],
7276                 t = o[1],
7277                 b = t+el.offsetHeight,
7278                 r = l+el.offsetWidth;
7279
7280             var ch = c.clientHeight;
7281             var ct = parseInt(c.scrollTop, 10);
7282             var cl = parseInt(c.scrollLeft, 10);
7283             var cb = ct + ch;
7284             var cr = cl + c.clientWidth;
7285
7286             if(t < ct){
7287                 c.scrollTop = t;
7288             }else if(b > cb){
7289                 c.scrollTop = b-ch;
7290             }
7291
7292             if(hscroll !== false){
7293                 if(l < cl){
7294                     c.scrollLeft = l;
7295                 }else if(r > cr){
7296                     c.scrollLeft = r-c.clientWidth;
7297                 }
7298             }
7299             return this;
7300         },
7301
7302         // private
7303         scrollChildIntoView : function(child, hscroll){
7304             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7305         },
7306
7307         /**
7308          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7309          * the new height may not be available immediately.
7310          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7311          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7312          * @param {Function} onComplete (optional) Function to call when animation completes
7313          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7314          * @return {Roo.Element} this
7315          */
7316         autoHeight : function(animate, duration, onComplete, easing){
7317             var oldHeight = this.getHeight();
7318             this.clip();
7319             this.setHeight(1); // force clipping
7320             setTimeout(function(){
7321                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7322                 if(!animate){
7323                     this.setHeight(height);
7324                     this.unclip();
7325                     if(typeof onComplete == "function"){
7326                         onComplete();
7327                     }
7328                 }else{
7329                     this.setHeight(oldHeight); // restore original height
7330                     this.setHeight(height, animate, duration, function(){
7331                         this.unclip();
7332                         if(typeof onComplete == "function") onComplete();
7333                     }.createDelegate(this), easing);
7334                 }
7335             }.createDelegate(this), 0);
7336             return this;
7337         },
7338
7339         /**
7340          * Returns true if this element is an ancestor of the passed element
7341          * @param {HTMLElement/String} el The element to check
7342          * @return {Boolean} True if this element is an ancestor of el, else false
7343          */
7344         contains : function(el){
7345             if(!el){return false;}
7346             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7347         },
7348
7349         /**
7350          * Checks whether the element is currently visible using both visibility and display properties.
7351          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7352          * @return {Boolean} True if the element is currently visible, else false
7353          */
7354         isVisible : function(deep) {
7355             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7356             if(deep !== true || !vis){
7357                 return vis;
7358             }
7359             var p = this.dom.parentNode;
7360             while(p && p.tagName.toLowerCase() != "body"){
7361                 if(!Roo.fly(p, '_isVisible').isVisible()){
7362                     return false;
7363                 }
7364                 p = p.parentNode;
7365             }
7366             return true;
7367         },
7368
7369         /**
7370          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7371          * @param {String} selector The CSS selector
7372          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7373          * @return {CompositeElement/CompositeElementLite} The composite element
7374          */
7375         select : function(selector, unique){
7376             return El.select(selector, unique, this.dom);
7377         },
7378
7379         /**
7380          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7381          * @param {String} selector The CSS selector
7382          * @return {Array} An array of the matched nodes
7383          */
7384         query : function(selector, unique){
7385             return Roo.DomQuery.select(selector, this.dom);
7386         },
7387
7388         /**
7389          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7390          * @param {String} selector The CSS selector
7391          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7392          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7393          */
7394         child : function(selector, returnDom){
7395             var n = Roo.DomQuery.selectNode(selector, this.dom);
7396             return returnDom ? n : Roo.get(n);
7397         },
7398
7399         /**
7400          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7401          * @param {String} selector The CSS selector
7402          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7403          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7404          */
7405         down : function(selector, returnDom){
7406             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7407             return returnDom ? n : Roo.get(n);
7408         },
7409
7410         /**
7411          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7412          * @param {String} group The group the DD object is member of
7413          * @param {Object} config The DD config object
7414          * @param {Object} overrides An object containing methods to override/implement on the DD object
7415          * @return {Roo.dd.DD} The DD object
7416          */
7417         initDD : function(group, config, overrides){
7418             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7419             return Roo.apply(dd, overrides);
7420         },
7421
7422         /**
7423          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7424          * @param {String} group The group the DDProxy object is member of
7425          * @param {Object} config The DDProxy config object
7426          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7427          * @return {Roo.dd.DDProxy} The DDProxy object
7428          */
7429         initDDProxy : function(group, config, overrides){
7430             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7431             return Roo.apply(dd, overrides);
7432         },
7433
7434         /**
7435          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7436          * @param {String} group The group the DDTarget object is member of
7437          * @param {Object} config The DDTarget config object
7438          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7439          * @return {Roo.dd.DDTarget} The DDTarget object
7440          */
7441         initDDTarget : function(group, config, overrides){
7442             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7443             return Roo.apply(dd, overrides);
7444         },
7445
7446         /**
7447          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7448          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7449          * @param {Boolean} visible Whether the element is visible
7450          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7451          * @return {Roo.Element} this
7452          */
7453          setVisible : function(visible, animate){
7454             if(!animate || !A){
7455                 if(this.visibilityMode == El.DISPLAY){
7456                     this.setDisplayed(visible);
7457                 }else{
7458                     this.fixDisplay();
7459                     this.dom.style.visibility = visible ? "visible" : "hidden";
7460                 }
7461             }else{
7462                 // closure for composites
7463                 var dom = this.dom;
7464                 var visMode = this.visibilityMode;
7465                 if(visible){
7466                     this.setOpacity(.01);
7467                     this.setVisible(true);
7468                 }
7469                 this.anim({opacity: { to: (visible?1:0) }},
7470                       this.preanim(arguments, 1),
7471                       null, .35, 'easeIn', function(){
7472                          if(!visible){
7473                              if(visMode == El.DISPLAY){
7474                                  dom.style.display = "none";
7475                              }else{
7476                                  dom.style.visibility = "hidden";
7477                              }
7478                              Roo.get(dom).setOpacity(1);
7479                          }
7480                      });
7481             }
7482             return this;
7483         },
7484
7485         /**
7486          * Returns true if display is not "none"
7487          * @return {Boolean}
7488          */
7489         isDisplayed : function() {
7490             return this.getStyle("display") != "none";
7491         },
7492
7493         /**
7494          * Toggles the element's visibility or display, depending on visibility mode.
7495          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7496          * @return {Roo.Element} this
7497          */
7498         toggle : function(animate){
7499             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7500             return this;
7501         },
7502
7503         /**
7504          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7505          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7506          * @return {Roo.Element} this
7507          */
7508         setDisplayed : function(value) {
7509             if(typeof value == "boolean"){
7510                value = value ? this.originalDisplay : "none";
7511             }
7512             this.setStyle("display", value);
7513             return this;
7514         },
7515
7516         /**
7517          * Tries to focus the element. Any exceptions are caught and ignored.
7518          * @return {Roo.Element} this
7519          */
7520         focus : function() {
7521             try{
7522                 this.dom.focus();
7523             }catch(e){}
7524             return this;
7525         },
7526
7527         /**
7528          * Tries to blur the element. Any exceptions are caught and ignored.
7529          * @return {Roo.Element} this
7530          */
7531         blur : function() {
7532             try{
7533                 this.dom.blur();
7534             }catch(e){}
7535             return this;
7536         },
7537
7538         /**
7539          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7540          * @param {String/Array} className The CSS class to add, or an array of classes
7541          * @return {Roo.Element} this
7542          */
7543         addClass : function(className){
7544             if(className instanceof Array){
7545                 for(var i = 0, len = className.length; i < len; i++) {
7546                     this.addClass(className[i]);
7547                 }
7548             }else{
7549                 if(className && !this.hasClass(className)){
7550                     this.dom.className = this.dom.className + " " + className;
7551                 }
7552             }
7553             return this;
7554         },
7555
7556         /**
7557          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7558          * @param {String/Array} className The CSS class to add, or an array of classes
7559          * @return {Roo.Element} this
7560          */
7561         radioClass : function(className){
7562             var siblings = this.dom.parentNode.childNodes;
7563             for(var i = 0; i < siblings.length; i++) {
7564                 var s = siblings[i];
7565                 if(s.nodeType == 1){
7566                     Roo.get(s).removeClass(className);
7567                 }
7568             }
7569             this.addClass(className);
7570             return this;
7571         },
7572
7573         /**
7574          * Removes one or more CSS classes from the element.
7575          * @param {String/Array} className The CSS class to remove, or an array of classes
7576          * @return {Roo.Element} this
7577          */
7578         removeClass : function(className){
7579             if(!className || !this.dom.className){
7580                 return this;
7581             }
7582             if(className instanceof Array){
7583                 for(var i = 0, len = className.length; i < len; i++) {
7584                     this.removeClass(className[i]);
7585                 }
7586             }else{
7587                 if(this.hasClass(className)){
7588                     var re = this.classReCache[className];
7589                     if (!re) {
7590                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7591                        this.classReCache[className] = re;
7592                     }
7593                     this.dom.className =
7594                         this.dom.className.replace(re, " ");
7595                 }
7596             }
7597             return this;
7598         },
7599
7600         // private
7601         classReCache: {},
7602
7603         /**
7604          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7605          * @param {String} className The CSS class to toggle
7606          * @return {Roo.Element} this
7607          */
7608         toggleClass : function(className){
7609             if(this.hasClass(className)){
7610                 this.removeClass(className);
7611             }else{
7612                 this.addClass(className);
7613             }
7614             return this;
7615         },
7616
7617         /**
7618          * Checks if the specified CSS class exists on this element's DOM node.
7619          * @param {String} className The CSS class to check for
7620          * @return {Boolean} True if the class exists, else false
7621          */
7622         hasClass : function(className){
7623             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7624         },
7625
7626         /**
7627          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7628          * @param {String} oldClassName The CSS class to replace
7629          * @param {String} newClassName The replacement CSS class
7630          * @return {Roo.Element} this
7631          */
7632         replaceClass : function(oldClassName, newClassName){
7633             this.removeClass(oldClassName);
7634             this.addClass(newClassName);
7635             return this;
7636         },
7637
7638         /**
7639          * Returns an object with properties matching the styles requested.
7640          * For example, el.getStyles('color', 'font-size', 'width') might return
7641          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7642          * @param {String} style1 A style name
7643          * @param {String} style2 A style name
7644          * @param {String} etc.
7645          * @return {Object} The style object
7646          */
7647         getStyles : function(){
7648             var a = arguments, len = a.length, r = {};
7649             for(var i = 0; i < len; i++){
7650                 r[a[i]] = this.getStyle(a[i]);
7651             }
7652             return r;
7653         },
7654
7655         /**
7656          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7657          * @param {String} property The style property whose value is returned.
7658          * @return {String} The current value of the style property for this element.
7659          */
7660         getStyle : function(){
7661             return view && view.getComputedStyle ?
7662                 function(prop){
7663                     var el = this.dom, v, cs, camel;
7664                     if(prop == 'float'){
7665                         prop = "cssFloat";
7666                     }
7667                     if(el.style && (v = el.style[prop])){
7668                         return v;
7669                     }
7670                     if(cs = view.getComputedStyle(el, "")){
7671                         if(!(camel = propCache[prop])){
7672                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7673                         }
7674                         return cs[camel];
7675                     }
7676                     return null;
7677                 } :
7678                 function(prop){
7679                     var el = this.dom, v, cs, camel;
7680                     if(prop == 'opacity'){
7681                         if(typeof el.style.filter == 'string'){
7682                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7683                             if(m){
7684                                 var fv = parseFloat(m[1]);
7685                                 if(!isNaN(fv)){
7686                                     return fv ? fv / 100 : 0;
7687                                 }
7688                             }
7689                         }
7690                         return 1;
7691                     }else if(prop == 'float'){
7692                         prop = "styleFloat";
7693                     }
7694                     if(!(camel = propCache[prop])){
7695                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7696                     }
7697                     if(v = el.style[camel]){
7698                         return v;
7699                     }
7700                     if(cs = el.currentStyle){
7701                         return cs[camel];
7702                     }
7703                     return null;
7704                 };
7705         }(),
7706
7707         /**
7708          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7709          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7710          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7711          * @return {Roo.Element} this
7712          */
7713         setStyle : function(prop, value){
7714             if(typeof prop == "string"){
7715                 
7716                 if (prop == 'float') {
7717                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7718                     return this;
7719                 }
7720                 
7721                 var camel;
7722                 if(!(camel = propCache[prop])){
7723                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7724                 }
7725                 
7726                 if(camel == 'opacity') {
7727                     this.setOpacity(value);
7728                 }else{
7729                     this.dom.style[camel] = value;
7730                 }
7731             }else{
7732                 for(var style in prop){
7733                     if(typeof prop[style] != "function"){
7734                        this.setStyle(style, prop[style]);
7735                     }
7736                 }
7737             }
7738             return this;
7739         },
7740
7741         /**
7742          * More flexible version of {@link #setStyle} for setting style properties.
7743          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7744          * a function which returns such a specification.
7745          * @return {Roo.Element} this
7746          */
7747         applyStyles : function(style){
7748             Roo.DomHelper.applyStyles(this.dom, style);
7749             return this;
7750         },
7751
7752         /**
7753           * 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).
7754           * @return {Number} The X position of the element
7755           */
7756         getX : function(){
7757             return D.getX(this.dom);
7758         },
7759
7760         /**
7761           * 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).
7762           * @return {Number} The Y position of the element
7763           */
7764         getY : function(){
7765             return D.getY(this.dom);
7766         },
7767
7768         /**
7769           * 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).
7770           * @return {Array} The XY position of the element
7771           */
7772         getXY : function(){
7773             return D.getXY(this.dom);
7774         },
7775
7776         /**
7777          * 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).
7778          * @param {Number} The X position of the element
7779          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7780          * @return {Roo.Element} this
7781          */
7782         setX : function(x, animate){
7783             if(!animate || !A){
7784                 D.setX(this.dom, x);
7785             }else{
7786                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7787             }
7788             return this;
7789         },
7790
7791         /**
7792          * 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).
7793          * @param {Number} The Y position of the element
7794          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7795          * @return {Roo.Element} this
7796          */
7797         setY : function(y, animate){
7798             if(!animate || !A){
7799                 D.setY(this.dom, y);
7800             }else{
7801                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7802             }
7803             return this;
7804         },
7805
7806         /**
7807          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7808          * @param {String} left The left CSS property value
7809          * @return {Roo.Element} this
7810          */
7811         setLeft : function(left){
7812             this.setStyle("left", this.addUnits(left));
7813             return this;
7814         },
7815
7816         /**
7817          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7818          * @param {String} top The top CSS property value
7819          * @return {Roo.Element} this
7820          */
7821         setTop : function(top){
7822             this.setStyle("top", this.addUnits(top));
7823             return this;
7824         },
7825
7826         /**
7827          * Sets the element's CSS right style.
7828          * @param {String} right The right CSS property value
7829          * @return {Roo.Element} this
7830          */
7831         setRight : function(right){
7832             this.setStyle("right", this.addUnits(right));
7833             return this;
7834         },
7835
7836         /**
7837          * Sets the element's CSS bottom style.
7838          * @param {String} bottom The bottom CSS property value
7839          * @return {Roo.Element} this
7840          */
7841         setBottom : function(bottom){
7842             this.setStyle("bottom", this.addUnits(bottom));
7843             return this;
7844         },
7845
7846         /**
7847          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7848          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7849          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7850          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7851          * @return {Roo.Element} this
7852          */
7853         setXY : function(pos, animate){
7854             if(!animate || !A){
7855                 D.setXY(this.dom, pos);
7856             }else{
7857                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7858             }
7859             return this;
7860         },
7861
7862         /**
7863          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7864          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7865          * @param {Number} x X value for new position (coordinates are page-based)
7866          * @param {Number} y Y value for new position (coordinates are page-based)
7867          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7868          * @return {Roo.Element} this
7869          */
7870         setLocation : function(x, y, animate){
7871             this.setXY([x, y], this.preanim(arguments, 2));
7872             return this;
7873         },
7874
7875         /**
7876          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7877          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7878          * @param {Number} x X value for new position (coordinates are page-based)
7879          * @param {Number} y Y value for new position (coordinates are page-based)
7880          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7881          * @return {Roo.Element} this
7882          */
7883         moveTo : function(x, y, animate){
7884             this.setXY([x, y], this.preanim(arguments, 2));
7885             return this;
7886         },
7887
7888         /**
7889          * Returns the region of the given element.
7890          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7891          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7892          */
7893         getRegion : function(){
7894             return D.getRegion(this.dom);
7895         },
7896
7897         /**
7898          * Returns the offset height of the element
7899          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7900          * @return {Number} The element's height
7901          */
7902         getHeight : function(contentHeight){
7903             var h = this.dom.offsetHeight || 0;
7904             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7905         },
7906
7907         /**
7908          * Returns the offset width of the element
7909          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7910          * @return {Number} The element's width
7911          */
7912         getWidth : function(contentWidth){
7913             var w = this.dom.offsetWidth || 0;
7914             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7915         },
7916
7917         /**
7918          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7919          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7920          * if a height has not been set using CSS.
7921          * @return {Number}
7922          */
7923         getComputedHeight : function(){
7924             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7925             if(!h){
7926                 h = parseInt(this.getStyle('height'), 10) || 0;
7927                 if(!this.isBorderBox()){
7928                     h += this.getFrameWidth('tb');
7929                 }
7930             }
7931             return h;
7932         },
7933
7934         /**
7935          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7936          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7937          * if a width has not been set using CSS.
7938          * @return {Number}
7939          */
7940         getComputedWidth : function(){
7941             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7942             if(!w){
7943                 w = parseInt(this.getStyle('width'), 10) || 0;
7944                 if(!this.isBorderBox()){
7945                     w += this.getFrameWidth('lr');
7946                 }
7947             }
7948             return w;
7949         },
7950
7951         /**
7952          * Returns the size of the element.
7953          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7954          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7955          */
7956         getSize : function(contentSize){
7957             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7958         },
7959
7960         /**
7961          * Returns the width and height of the viewport.
7962          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7963          */
7964         getViewSize : function(){
7965             var d = this.dom, doc = document, aw = 0, ah = 0;
7966             if(d == doc || d == doc.body){
7967                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7968             }else{
7969                 return {
7970                     width : d.clientWidth,
7971                     height: d.clientHeight
7972                 };
7973             }
7974         },
7975
7976         /**
7977          * Returns the value of the "value" attribute
7978          * @param {Boolean} asNumber true to parse the value as a number
7979          * @return {String/Number}
7980          */
7981         getValue : function(asNumber){
7982             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7983         },
7984
7985         // private
7986         adjustWidth : function(width){
7987             if(typeof width == "number"){
7988                 if(this.autoBoxAdjust && !this.isBorderBox()){
7989                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7990                 }
7991                 if(width < 0){
7992                     width = 0;
7993                 }
7994             }
7995             return width;
7996         },
7997
7998         // private
7999         adjustHeight : function(height){
8000             if(typeof height == "number"){
8001                if(this.autoBoxAdjust && !this.isBorderBox()){
8002                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8003                }
8004                if(height < 0){
8005                    height = 0;
8006                }
8007             }
8008             return height;
8009         },
8010
8011         /**
8012          * Set the width of the element
8013          * @param {Number} width The new width
8014          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8015          * @return {Roo.Element} this
8016          */
8017         setWidth : function(width, animate){
8018             width = this.adjustWidth(width);
8019             if(!animate || !A){
8020                 this.dom.style.width = this.addUnits(width);
8021             }else{
8022                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8023             }
8024             return this;
8025         },
8026
8027         /**
8028          * Set the height of the element
8029          * @param {Number} height The new height
8030          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8031          * @return {Roo.Element} this
8032          */
8033          setHeight : function(height, animate){
8034             height = this.adjustHeight(height);
8035             if(!animate || !A){
8036                 this.dom.style.height = this.addUnits(height);
8037             }else{
8038                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8039             }
8040             return this;
8041         },
8042
8043         /**
8044          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8045          * @param {Number} width The new width
8046          * @param {Number} height The new height
8047          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8048          * @return {Roo.Element} this
8049          */
8050          setSize : function(width, height, animate){
8051             if(typeof width == "object"){ // in case of object from getSize()
8052                 height = width.height; width = width.width;
8053             }
8054             width = this.adjustWidth(width); height = this.adjustHeight(height);
8055             if(!animate || !A){
8056                 this.dom.style.width = this.addUnits(width);
8057                 this.dom.style.height = this.addUnits(height);
8058             }else{
8059                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8060             }
8061             return this;
8062         },
8063
8064         /**
8065          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8066          * @param {Number} x X value for new position (coordinates are page-based)
8067          * @param {Number} y Y value for new position (coordinates are page-based)
8068          * @param {Number} width The new width
8069          * @param {Number} height The new height
8070          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8071          * @return {Roo.Element} this
8072          */
8073         setBounds : function(x, y, width, height, animate){
8074             if(!animate || !A){
8075                 this.setSize(width, height);
8076                 this.setLocation(x, y);
8077             }else{
8078                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8079                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8080                               this.preanim(arguments, 4), 'motion');
8081             }
8082             return this;
8083         },
8084
8085         /**
8086          * 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.
8087          * @param {Roo.lib.Region} region The region to fill
8088          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8089          * @return {Roo.Element} this
8090          */
8091         setRegion : function(region, animate){
8092             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8093             return this;
8094         },
8095
8096         /**
8097          * Appends an event handler
8098          *
8099          * @param {String}   eventName     The type of event to append
8100          * @param {Function} fn        The method the event invokes
8101          * @param {Object} scope       (optional) The scope (this object) of the fn
8102          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8103          */
8104         addListener : function(eventName, fn, scope, options){
8105             if (this.dom) {
8106                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8107             }
8108         },
8109
8110         /**
8111          * Removes an event handler from this element
8112          * @param {String} eventName the type of event to remove
8113          * @param {Function} fn the method the event invokes
8114          * @return {Roo.Element} this
8115          */
8116         removeListener : function(eventName, fn){
8117             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8118             return this;
8119         },
8120
8121         /**
8122          * Removes all previous added listeners from this element
8123          * @return {Roo.Element} this
8124          */
8125         removeAllListeners : function(){
8126             E.purgeElement(this.dom);
8127             return this;
8128         },
8129
8130         relayEvent : function(eventName, observable){
8131             this.on(eventName, function(e){
8132                 observable.fireEvent(eventName, e);
8133             });
8134         },
8135
8136         /**
8137          * Set the opacity of the element
8138          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8139          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8140          * @return {Roo.Element} this
8141          */
8142          setOpacity : function(opacity, animate){
8143             if(!animate || !A){
8144                 var s = this.dom.style;
8145                 if(Roo.isIE){
8146                     s.zoom = 1;
8147                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8148                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8149                 }else{
8150                     s.opacity = opacity;
8151                 }
8152             }else{
8153                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8154             }
8155             return this;
8156         },
8157
8158         /**
8159          * Gets the left X coordinate
8160          * @param {Boolean} local True to get the local css position instead of page coordinate
8161          * @return {Number}
8162          */
8163         getLeft : function(local){
8164             if(!local){
8165                 return this.getX();
8166             }else{
8167                 return parseInt(this.getStyle("left"), 10) || 0;
8168             }
8169         },
8170
8171         /**
8172          * Gets the right X coordinate of the element (element X position + element width)
8173          * @param {Boolean} local True to get the local css position instead of page coordinate
8174          * @return {Number}
8175          */
8176         getRight : function(local){
8177             if(!local){
8178                 return this.getX() + this.getWidth();
8179             }else{
8180                 return (this.getLeft(true) + this.getWidth()) || 0;
8181             }
8182         },
8183
8184         /**
8185          * Gets the top Y coordinate
8186          * @param {Boolean} local True to get the local css position instead of page coordinate
8187          * @return {Number}
8188          */
8189         getTop : function(local) {
8190             if(!local){
8191                 return this.getY();
8192             }else{
8193                 return parseInt(this.getStyle("top"), 10) || 0;
8194             }
8195         },
8196
8197         /**
8198          * Gets the bottom Y coordinate of the element (element Y position + element height)
8199          * @param {Boolean} local True to get the local css position instead of page coordinate
8200          * @return {Number}
8201          */
8202         getBottom : function(local){
8203             if(!local){
8204                 return this.getY() + this.getHeight();
8205             }else{
8206                 return (this.getTop(true) + this.getHeight()) || 0;
8207             }
8208         },
8209
8210         /**
8211         * Initializes positioning on this element. If a desired position is not passed, it will make the
8212         * the element positioned relative IF it is not already positioned.
8213         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8214         * @param {Number} zIndex (optional) The zIndex to apply
8215         * @param {Number} x (optional) Set the page X position
8216         * @param {Number} y (optional) Set the page Y position
8217         */
8218         position : function(pos, zIndex, x, y){
8219             if(!pos){
8220                if(this.getStyle('position') == 'static'){
8221                    this.setStyle('position', 'relative');
8222                }
8223             }else{
8224                 this.setStyle("position", pos);
8225             }
8226             if(zIndex){
8227                 this.setStyle("z-index", zIndex);
8228             }
8229             if(x !== undefined && y !== undefined){
8230                 this.setXY([x, y]);
8231             }else if(x !== undefined){
8232                 this.setX(x);
8233             }else if(y !== undefined){
8234                 this.setY(y);
8235             }
8236         },
8237
8238         /**
8239         * Clear positioning back to the default when the document was loaded
8240         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8241         * @return {Roo.Element} this
8242          */
8243         clearPositioning : function(value){
8244             value = value ||'';
8245             this.setStyle({
8246                 "left": value,
8247                 "right": value,
8248                 "top": value,
8249                 "bottom": value,
8250                 "z-index": "",
8251                 "position" : "static"
8252             });
8253             return this;
8254         },
8255
8256         /**
8257         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8258         * snapshot before performing an update and then restoring the element.
8259         * @return {Object}
8260         */
8261         getPositioning : function(){
8262             var l = this.getStyle("left");
8263             var t = this.getStyle("top");
8264             return {
8265                 "position" : this.getStyle("position"),
8266                 "left" : l,
8267                 "right" : l ? "" : this.getStyle("right"),
8268                 "top" : t,
8269                 "bottom" : t ? "" : this.getStyle("bottom"),
8270                 "z-index" : this.getStyle("z-index")
8271             };
8272         },
8273
8274         /**
8275          * Gets the width of the border(s) for the specified side(s)
8276          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8277          * passing lr would get the border (l)eft width + the border (r)ight width.
8278          * @return {Number} The width of the sides passed added together
8279          */
8280         getBorderWidth : function(side){
8281             return this.addStyles(side, El.borders);
8282         },
8283
8284         /**
8285          * Gets the width of the padding(s) for the specified side(s)
8286          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8287          * passing lr would get the padding (l)eft + the padding (r)ight.
8288          * @return {Number} The padding of the sides passed added together
8289          */
8290         getPadding : function(side){
8291             return this.addStyles(side, El.paddings);
8292         },
8293
8294         /**
8295         * Set positioning with an object returned by getPositioning().
8296         * @param {Object} posCfg
8297         * @return {Roo.Element} this
8298          */
8299         setPositioning : function(pc){
8300             this.applyStyles(pc);
8301             if(pc.right == "auto"){
8302                 this.dom.style.right = "";
8303             }
8304             if(pc.bottom == "auto"){
8305                 this.dom.style.bottom = "";
8306             }
8307             return this;
8308         },
8309
8310         // private
8311         fixDisplay : function(){
8312             if(this.getStyle("display") == "none"){
8313                 this.setStyle("visibility", "hidden");
8314                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8315                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8316                     this.setStyle("display", "block");
8317                 }
8318             }
8319         },
8320
8321         /**
8322          * Quick set left and top adding default units
8323          * @param {String} left The left CSS property value
8324          * @param {String} top The top CSS property value
8325          * @return {Roo.Element} this
8326          */
8327          setLeftTop : function(left, top){
8328             this.dom.style.left = this.addUnits(left);
8329             this.dom.style.top = this.addUnits(top);
8330             return this;
8331         },
8332
8333         /**
8334          * Move this element relative to its current position.
8335          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8336          * @param {Number} distance How far to move the element in pixels
8337          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8338          * @return {Roo.Element} this
8339          */
8340          move : function(direction, distance, animate){
8341             var xy = this.getXY();
8342             direction = direction.toLowerCase();
8343             switch(direction){
8344                 case "l":
8345                 case "left":
8346                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8347                     break;
8348                case "r":
8349                case "right":
8350                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8351                     break;
8352                case "t":
8353                case "top":
8354                case "up":
8355                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8356                     break;
8357                case "b":
8358                case "bottom":
8359                case "down":
8360                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8361                     break;
8362             }
8363             return this;
8364         },
8365
8366         /**
8367          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8368          * @return {Roo.Element} this
8369          */
8370         clip : function(){
8371             if(!this.isClipped){
8372                this.isClipped = true;
8373                this.originalClip = {
8374                    "o": this.getStyle("overflow"),
8375                    "x": this.getStyle("overflow-x"),
8376                    "y": this.getStyle("overflow-y")
8377                };
8378                this.setStyle("overflow", "hidden");
8379                this.setStyle("overflow-x", "hidden");
8380                this.setStyle("overflow-y", "hidden");
8381             }
8382             return this;
8383         },
8384
8385         /**
8386          *  Return clipping (overflow) to original clipping before clip() was called
8387          * @return {Roo.Element} this
8388          */
8389         unclip : function(){
8390             if(this.isClipped){
8391                 this.isClipped = false;
8392                 var o = this.originalClip;
8393                 if(o.o){this.setStyle("overflow", o.o);}
8394                 if(o.x){this.setStyle("overflow-x", o.x);}
8395                 if(o.y){this.setStyle("overflow-y", o.y);}
8396             }
8397             return this;
8398         },
8399
8400
8401         /**
8402          * Gets the x,y coordinates specified by the anchor position on the element.
8403          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8404          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8405          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8406          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8407          * @return {Array} [x, y] An array containing the element's x and y coordinates
8408          */
8409         getAnchorXY : function(anchor, local, s){
8410             //Passing a different size is useful for pre-calculating anchors,
8411             //especially for anchored animations that change the el size.
8412
8413             var w, h, vp = false;
8414             if(!s){
8415                 var d = this.dom;
8416                 if(d == document.body || d == document){
8417                     vp = true;
8418                     w = D.getViewWidth(); h = D.getViewHeight();
8419                 }else{
8420                     w = this.getWidth(); h = this.getHeight();
8421                 }
8422             }else{
8423                 w = s.width;  h = s.height;
8424             }
8425             var x = 0, y = 0, r = Math.round;
8426             switch((anchor || "tl").toLowerCase()){
8427                 case "c":
8428                     x = r(w*.5);
8429                     y = r(h*.5);
8430                 break;
8431                 case "t":
8432                     x = r(w*.5);
8433                     y = 0;
8434                 break;
8435                 case "l":
8436                     x = 0;
8437                     y = r(h*.5);
8438                 break;
8439                 case "r":
8440                     x = w;
8441                     y = r(h*.5);
8442                 break;
8443                 case "b":
8444                     x = r(w*.5);
8445                     y = h;
8446                 break;
8447                 case "tl":
8448                     x = 0;
8449                     y = 0;
8450                 break;
8451                 case "bl":
8452                     x = 0;
8453                     y = h;
8454                 break;
8455                 case "br":
8456                     x = w;
8457                     y = h;
8458                 break;
8459                 case "tr":
8460                     x = w;
8461                     y = 0;
8462                 break;
8463             }
8464             if(local === true){
8465                 return [x, y];
8466             }
8467             if(vp){
8468                 var sc = this.getScroll();
8469                 return [x + sc.left, y + sc.top];
8470             }
8471             //Add the element's offset xy
8472             var o = this.getXY();
8473             return [x+o[0], y+o[1]];
8474         },
8475
8476         /**
8477          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8478          * supported position values.
8479          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8480          * @param {String} position The position to align to.
8481          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8482          * @return {Array} [x, y]
8483          */
8484         getAlignToXY : function(el, p, o){
8485             el = Roo.get(el);
8486             var d = this.dom;
8487             if(!el.dom){
8488                 throw "Element.alignTo with an element that doesn't exist";
8489             }
8490             var c = false; //constrain to viewport
8491             var p1 = "", p2 = "";
8492             o = o || [0,0];
8493
8494             if(!p){
8495                 p = "tl-bl";
8496             }else if(p == "?"){
8497                 p = "tl-bl?";
8498             }else if(p.indexOf("-") == -1){
8499                 p = "tl-" + p;
8500             }
8501             p = p.toLowerCase();
8502             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8503             if(!m){
8504                throw "Element.alignTo with an invalid alignment " + p;
8505             }
8506             p1 = m[1]; p2 = m[2]; c = !!m[3];
8507
8508             //Subtract the aligned el's internal xy from the target's offset xy
8509             //plus custom offset to get the aligned el's new offset xy
8510             var a1 = this.getAnchorXY(p1, true);
8511             var a2 = el.getAnchorXY(p2, false);
8512             var x = a2[0] - a1[0] + o[0];
8513             var y = a2[1] - a1[1] + o[1];
8514             if(c){
8515                 //constrain the aligned el to viewport if necessary
8516                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8517                 // 5px of margin for ie
8518                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8519
8520                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8521                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8522                 //otherwise swap the aligned el to the opposite border of the target.
8523                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8524                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8525                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8526                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8527
8528                var doc = document;
8529                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8530                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8531
8532                if((x+w) > dw + scrollX){
8533                     x = swapX ? r.left-w : dw+scrollX-w;
8534                 }
8535                if(x < scrollX){
8536                    x = swapX ? r.right : scrollX;
8537                }
8538                if((y+h) > dh + scrollY){
8539                     y = swapY ? r.top-h : dh+scrollY-h;
8540                 }
8541                if (y < scrollY){
8542                    y = swapY ? r.bottom : scrollY;
8543                }
8544             }
8545             return [x,y];
8546         },
8547
8548         // private
8549         getConstrainToXY : function(){
8550             var os = {top:0, left:0, bottom:0, right: 0};
8551
8552             return function(el, local, offsets, proposedXY){
8553                 el = Roo.get(el);
8554                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8555
8556                 var vw, vh, vx = 0, vy = 0;
8557                 if(el.dom == document.body || el.dom == document){
8558                     vw = Roo.lib.Dom.getViewWidth();
8559                     vh = Roo.lib.Dom.getViewHeight();
8560                 }else{
8561                     vw = el.dom.clientWidth;
8562                     vh = el.dom.clientHeight;
8563                     if(!local){
8564                         var vxy = el.getXY();
8565                         vx = vxy[0];
8566                         vy = vxy[1];
8567                     }
8568                 }
8569
8570                 var s = el.getScroll();
8571
8572                 vx += offsets.left + s.left;
8573                 vy += offsets.top + s.top;
8574
8575                 vw -= offsets.right;
8576                 vh -= offsets.bottom;
8577
8578                 var vr = vx+vw;
8579                 var vb = vy+vh;
8580
8581                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8582                 var x = xy[0], y = xy[1];
8583                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8584
8585                 // only move it if it needs it
8586                 var moved = false;
8587
8588                 // first validate right/bottom
8589                 if((x + w) > vr){
8590                     x = vr - w;
8591                     moved = true;
8592                 }
8593                 if((y + h) > vb){
8594                     y = vb - h;
8595                     moved = true;
8596                 }
8597                 // then make sure top/left isn't negative
8598                 if(x < vx){
8599                     x = vx;
8600                     moved = true;
8601                 }
8602                 if(y < vy){
8603                     y = vy;
8604                     moved = true;
8605                 }
8606                 return moved ? [x, y] : false;
8607             };
8608         }(),
8609
8610         // private
8611         adjustForConstraints : function(xy, parent, offsets){
8612             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8613         },
8614
8615         /**
8616          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8617          * document it aligns it to the viewport.
8618          * The position parameter is optional, and can be specified in any one of the following formats:
8619          * <ul>
8620          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8621          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8622          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8623          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8624          *   <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
8625          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8626          * </ul>
8627          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8628          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8629          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8630          * that specified in order to enforce the viewport constraints.
8631          * Following are all of the supported anchor positions:
8632     <pre>
8633     Value  Description
8634     -----  -----------------------------
8635     tl     The top left corner (default)
8636     t      The center of the top edge
8637     tr     The top right corner
8638     l      The center of the left edge
8639     c      In the center of the element
8640     r      The center of the right edge
8641     bl     The bottom left corner
8642     b      The center of the bottom edge
8643     br     The bottom right corner
8644     </pre>
8645     Example Usage:
8646     <pre><code>
8647     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8648     el.alignTo("other-el");
8649
8650     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8651     el.alignTo("other-el", "tr?");
8652
8653     // align the bottom right corner of el with the center left edge of other-el
8654     el.alignTo("other-el", "br-l?");
8655
8656     // align the center of el with the bottom left corner of other-el and
8657     // adjust the x position by -6 pixels (and the y position by 0)
8658     el.alignTo("other-el", "c-bl", [-6, 0]);
8659     </code></pre>
8660          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8661          * @param {String} position The position to align to.
8662          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8663          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8664          * @return {Roo.Element} this
8665          */
8666         alignTo : function(element, position, offsets, animate){
8667             var xy = this.getAlignToXY(element, position, offsets);
8668             this.setXY(xy, this.preanim(arguments, 3));
8669             return this;
8670         },
8671
8672         /**
8673          * Anchors an element to another element and realigns it when the window is resized.
8674          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8675          * @param {String} position The position to align to.
8676          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8677          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8678          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8679          * is a number, it is used as the buffer delay (defaults to 50ms).
8680          * @param {Function} callback The function to call after the animation finishes
8681          * @return {Roo.Element} this
8682          */
8683         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8684             var action = function(){
8685                 this.alignTo(el, alignment, offsets, animate);
8686                 Roo.callback(callback, this);
8687             };
8688             Roo.EventManager.onWindowResize(action, this);
8689             var tm = typeof monitorScroll;
8690             if(tm != 'undefined'){
8691                 Roo.EventManager.on(window, 'scroll', action, this,
8692                     {buffer: tm == 'number' ? monitorScroll : 50});
8693             }
8694             action.call(this); // align immediately
8695             return this;
8696         },
8697         /**
8698          * Clears any opacity settings from this element. Required in some cases for IE.
8699          * @return {Roo.Element} this
8700          */
8701         clearOpacity : function(){
8702             if (window.ActiveXObject) {
8703                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8704                     this.dom.style.filter = "";
8705                 }
8706             } else {
8707                 this.dom.style.opacity = "";
8708                 this.dom.style["-moz-opacity"] = "";
8709                 this.dom.style["-khtml-opacity"] = "";
8710             }
8711             return this;
8712         },
8713
8714         /**
8715          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8716          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8717          * @return {Roo.Element} this
8718          */
8719         hide : function(animate){
8720             this.setVisible(false, this.preanim(arguments, 0));
8721             return this;
8722         },
8723
8724         /**
8725         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8726         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8727          * @return {Roo.Element} this
8728          */
8729         show : function(animate){
8730             this.setVisible(true, this.preanim(arguments, 0));
8731             return this;
8732         },
8733
8734         /**
8735          * @private Test if size has a unit, otherwise appends the default
8736          */
8737         addUnits : function(size){
8738             return Roo.Element.addUnits(size, this.defaultUnit);
8739         },
8740
8741         /**
8742          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8743          * @return {Roo.Element} this
8744          */
8745         beginMeasure : function(){
8746             var el = this.dom;
8747             if(el.offsetWidth || el.offsetHeight){
8748                 return this; // offsets work already
8749             }
8750             var changed = [];
8751             var p = this.dom, b = document.body; // start with this element
8752             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8753                 var pe = Roo.get(p);
8754                 if(pe.getStyle('display') == 'none'){
8755                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8756                     p.style.visibility = "hidden";
8757                     p.style.display = "block";
8758                 }
8759                 p = p.parentNode;
8760             }
8761             this._measureChanged = changed;
8762             return this;
8763
8764         },
8765
8766         /**
8767          * Restores displays to before beginMeasure was called
8768          * @return {Roo.Element} this
8769          */
8770         endMeasure : function(){
8771             var changed = this._measureChanged;
8772             if(changed){
8773                 for(var i = 0, len = changed.length; i < len; i++) {
8774                     var r = changed[i];
8775                     r.el.style.visibility = r.visibility;
8776                     r.el.style.display = "none";
8777                 }
8778                 this._measureChanged = null;
8779             }
8780             return this;
8781         },
8782
8783         /**
8784         * Update the innerHTML of this element, optionally searching for and processing scripts
8785         * @param {String} html The new HTML
8786         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8787         * @param {Function} callback For async script loading you can be noticed when the update completes
8788         * @return {Roo.Element} this
8789          */
8790         update : function(html, loadScripts, callback){
8791             if(typeof html == "undefined"){
8792                 html = "";
8793             }
8794             if(loadScripts !== true){
8795                 this.dom.innerHTML = html;
8796                 if(typeof callback == "function"){
8797                     callback();
8798                 }
8799                 return this;
8800             }
8801             var id = Roo.id();
8802             var dom = this.dom;
8803
8804             html += '<span id="' + id + '"></span>';
8805
8806             E.onAvailable(id, function(){
8807                 var hd = document.getElementsByTagName("head")[0];
8808                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8809                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8810                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8811
8812                 var match;
8813                 while(match = re.exec(html)){
8814                     var attrs = match[1];
8815                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8816                     if(srcMatch && srcMatch[2]){
8817                        var s = document.createElement("script");
8818                        s.src = srcMatch[2];
8819                        var typeMatch = attrs.match(typeRe);
8820                        if(typeMatch && typeMatch[2]){
8821                            s.type = typeMatch[2];
8822                        }
8823                        hd.appendChild(s);
8824                     }else if(match[2] && match[2].length > 0){
8825                         if(window.execScript) {
8826                            window.execScript(match[2]);
8827                         } else {
8828                             /**
8829                              * eval:var:id
8830                              * eval:var:dom
8831                              * eval:var:html
8832                              * 
8833                              */
8834                            window.eval(match[2]);
8835                         }
8836                     }
8837                 }
8838                 var el = document.getElementById(id);
8839                 if(el){el.parentNode.removeChild(el);}
8840                 if(typeof callback == "function"){
8841                     callback();
8842                 }
8843             });
8844             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8845             return this;
8846         },
8847
8848         /**
8849          * Direct access to the UpdateManager update() method (takes the same parameters).
8850          * @param {String/Function} url The url for this request or a function to call to get the url
8851          * @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}
8852          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8853          * @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.
8854          * @return {Roo.Element} this
8855          */
8856         load : function(){
8857             var um = this.getUpdateManager();
8858             um.update.apply(um, arguments);
8859             return this;
8860         },
8861
8862         /**
8863         * Gets this element's UpdateManager
8864         * @return {Roo.UpdateManager} The UpdateManager
8865         */
8866         getUpdateManager : function(){
8867             if(!this.updateManager){
8868                 this.updateManager = new Roo.UpdateManager(this);
8869             }
8870             return this.updateManager;
8871         },
8872
8873         /**
8874          * Disables text selection for this element (normalized across browsers)
8875          * @return {Roo.Element} this
8876          */
8877         unselectable : function(){
8878             this.dom.unselectable = "on";
8879             this.swallowEvent("selectstart", true);
8880             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8881             this.addClass("x-unselectable");
8882             return this;
8883         },
8884
8885         /**
8886         * Calculates the x, y to center this element on the screen
8887         * @return {Array} The x, y values [x, y]
8888         */
8889         getCenterXY : function(){
8890             return this.getAlignToXY(document, 'c-c');
8891         },
8892
8893         /**
8894         * Centers the Element in either the viewport, or another Element.
8895         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8896         */
8897         center : function(centerIn){
8898             this.alignTo(centerIn || document, 'c-c');
8899             return this;
8900         },
8901
8902         /**
8903          * Tests various css rules/browsers to determine if this element uses a border box
8904          * @return {Boolean}
8905          */
8906         isBorderBox : function(){
8907             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8908         },
8909
8910         /**
8911          * Return a box {x, y, width, height} that can be used to set another elements
8912          * size/location to match this element.
8913          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8914          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8915          * @return {Object} box An object in the format {x, y, width, height}
8916          */
8917         getBox : function(contentBox, local){
8918             var xy;
8919             if(!local){
8920                 xy = this.getXY();
8921             }else{
8922                 var left = parseInt(this.getStyle("left"), 10) || 0;
8923                 var top = parseInt(this.getStyle("top"), 10) || 0;
8924                 xy = [left, top];
8925             }
8926             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8927             if(!contentBox){
8928                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8929             }else{
8930                 var l = this.getBorderWidth("l")+this.getPadding("l");
8931                 var r = this.getBorderWidth("r")+this.getPadding("r");
8932                 var t = this.getBorderWidth("t")+this.getPadding("t");
8933                 var b = this.getBorderWidth("b")+this.getPadding("b");
8934                 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)};
8935             }
8936             bx.right = bx.x + bx.width;
8937             bx.bottom = bx.y + bx.height;
8938             return bx;
8939         },
8940
8941         /**
8942          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8943          for more information about the sides.
8944          * @param {String} sides
8945          * @return {Number}
8946          */
8947         getFrameWidth : function(sides, onlyContentBox){
8948             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8949         },
8950
8951         /**
8952          * 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.
8953          * @param {Object} box The box to fill {x, y, width, height}
8954          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8955          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8956          * @return {Roo.Element} this
8957          */
8958         setBox : function(box, adjust, animate){
8959             var w = box.width, h = box.height;
8960             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8961                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8962                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8963             }
8964             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8965             return this;
8966         },
8967
8968         /**
8969          * Forces the browser to repaint this element
8970          * @return {Roo.Element} this
8971          */
8972          repaint : function(){
8973             var dom = this.dom;
8974             this.addClass("x-repaint");
8975             setTimeout(function(){
8976                 Roo.get(dom).removeClass("x-repaint");
8977             }, 1);
8978             return this;
8979         },
8980
8981         /**
8982          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8983          * then it returns the calculated width of the sides (see getPadding)
8984          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8985          * @return {Object/Number}
8986          */
8987         getMargins : function(side){
8988             if(!side){
8989                 return {
8990                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8991                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8992                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8993                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8994                 };
8995             }else{
8996                 return this.addStyles(side, El.margins);
8997              }
8998         },
8999
9000         // private
9001         addStyles : function(sides, styles){
9002             var val = 0, v, w;
9003             for(var i = 0, len = sides.length; i < len; i++){
9004                 v = this.getStyle(styles[sides.charAt(i)]);
9005                 if(v){
9006                      w = parseInt(v, 10);
9007                      if(w){ val += w; }
9008                 }
9009             }
9010             return val;
9011         },
9012
9013         /**
9014          * Creates a proxy element of this element
9015          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9016          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9017          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9018          * @return {Roo.Element} The new proxy element
9019          */
9020         createProxy : function(config, renderTo, matchBox){
9021             if(renderTo){
9022                 renderTo = Roo.getDom(renderTo);
9023             }else{
9024                 renderTo = document.body;
9025             }
9026             config = typeof config == "object" ?
9027                 config : {tag : "div", cls: config};
9028             var proxy = Roo.DomHelper.append(renderTo, config, true);
9029             if(matchBox){
9030                proxy.setBox(this.getBox());
9031             }
9032             return proxy;
9033         },
9034
9035         /**
9036          * Puts a mask over this element to disable user interaction. Requires core.css.
9037          * This method can only be applied to elements which accept child nodes.
9038          * @param {String} msg (optional) A message to display in the mask
9039          * @param {String} msgCls (optional) A css class to apply to the msg element
9040          * @return {Element} The mask  element
9041          */
9042         mask : function(msg, msgCls)
9043         {
9044             if(this.getStyle("position") == "static"){
9045                 this.setStyle("position", "relative");
9046             }
9047             if(!this._mask){
9048                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9049             }
9050             this.addClass("x-masked");
9051             this._mask.setDisplayed(true);
9052             
9053             // we wander
9054             var z = 0;
9055             var dom = this.dom
9056             while (dom && dom.style) {
9057                 if (!isNaN(parseInt(dom.style.zIndex))) {
9058                     z = Math.max(z, parseInt(dom.style.zIndex));
9059                 }
9060                 dom = dom.parentNode;
9061             }
9062             // if we are masking the body - then it hides everything..
9063             if (this.dom == document.body) {
9064                 z = 1000000;
9065                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9066                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9067             }
9068            
9069             if(typeof msg == 'string'){
9070                 if(!this._maskMsg){
9071                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9072                 }
9073                 var mm = this._maskMsg;
9074                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9075                 mm.dom.firstChild.innerHTML = msg;
9076                 mm.setDisplayed(true);
9077                 mm.center(this);
9078                 mm.setStyle('z-index', z + 102);
9079             }
9080             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9081                 this._mask.setHeight(this.getHeight());
9082             }
9083             this._mask.setStyle('z-index', z + 100);
9084             
9085             return this._mask;
9086         },
9087
9088         /**
9089          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9090          * it is cached for reuse.
9091          */
9092         unmask : function(removeEl){
9093             if(this._mask){
9094                 if(removeEl === true){
9095                     this._mask.remove();
9096                     delete this._mask;
9097                     if(this._maskMsg){
9098                         this._maskMsg.remove();
9099                         delete this._maskMsg;
9100                     }
9101                 }else{
9102                     this._mask.setDisplayed(false);
9103                     if(this._maskMsg){
9104                         this._maskMsg.setDisplayed(false);
9105                     }
9106                 }
9107             }
9108             this.removeClass("x-masked");
9109         },
9110
9111         /**
9112          * Returns true if this element is masked
9113          * @return {Boolean}
9114          */
9115         isMasked : function(){
9116             return this._mask && this._mask.isVisible();
9117         },
9118
9119         /**
9120          * Creates an iframe shim for this element to keep selects and other windowed objects from
9121          * showing through.
9122          * @return {Roo.Element} The new shim element
9123          */
9124         createShim : function(){
9125             var el = document.createElement('iframe');
9126             el.frameBorder = 'no';
9127             el.className = 'roo-shim';
9128             if(Roo.isIE && Roo.isSecure){
9129                 el.src = Roo.SSL_SECURE_URL;
9130             }
9131             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9132             shim.autoBoxAdjust = false;
9133             return shim;
9134         },
9135
9136         /**
9137          * Removes this element from the DOM and deletes it from the cache
9138          */
9139         remove : function(){
9140             if(this.dom.parentNode){
9141                 this.dom.parentNode.removeChild(this.dom);
9142             }
9143             delete El.cache[this.dom.id];
9144         },
9145
9146         /**
9147          * Sets up event handlers to add and remove a css class when the mouse is over this element
9148          * @param {String} className
9149          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9150          * mouseout events for children elements
9151          * @return {Roo.Element} this
9152          */
9153         addClassOnOver : function(className, preventFlicker){
9154             this.on("mouseover", function(){
9155                 Roo.fly(this, '_internal').addClass(className);
9156             }, this.dom);
9157             var removeFn = function(e){
9158                 if(preventFlicker !== true || !e.within(this, true)){
9159                     Roo.fly(this, '_internal').removeClass(className);
9160                 }
9161             };
9162             this.on("mouseout", removeFn, this.dom);
9163             return this;
9164         },
9165
9166         /**
9167          * Sets up event handlers to add and remove a css class when this element has the focus
9168          * @param {String} className
9169          * @return {Roo.Element} this
9170          */
9171         addClassOnFocus : function(className){
9172             this.on("focus", function(){
9173                 Roo.fly(this, '_internal').addClass(className);
9174             }, this.dom);
9175             this.on("blur", function(){
9176                 Roo.fly(this, '_internal').removeClass(className);
9177             }, this.dom);
9178             return this;
9179         },
9180         /**
9181          * 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)
9182          * @param {String} className
9183          * @return {Roo.Element} this
9184          */
9185         addClassOnClick : function(className){
9186             var dom = this.dom;
9187             this.on("mousedown", function(){
9188                 Roo.fly(dom, '_internal').addClass(className);
9189                 var d = Roo.get(document);
9190                 var fn = function(){
9191                     Roo.fly(dom, '_internal').removeClass(className);
9192                     d.removeListener("mouseup", fn);
9193                 };
9194                 d.on("mouseup", fn);
9195             });
9196             return this;
9197         },
9198
9199         /**
9200          * Stops the specified event from bubbling and optionally prevents the default action
9201          * @param {String} eventName
9202          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9203          * @return {Roo.Element} this
9204          */
9205         swallowEvent : function(eventName, preventDefault){
9206             var fn = function(e){
9207                 e.stopPropagation();
9208                 if(preventDefault){
9209                     e.preventDefault();
9210                 }
9211             };
9212             if(eventName instanceof Array){
9213                 for(var i = 0, len = eventName.length; i < len; i++){
9214                      this.on(eventName[i], fn);
9215                 }
9216                 return this;
9217             }
9218             this.on(eventName, fn);
9219             return this;
9220         },
9221
9222         /**
9223          * @private
9224          */
9225       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9226
9227         /**
9228          * Sizes this element to its parent element's dimensions performing
9229          * neccessary box adjustments.
9230          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9231          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9232          * @return {Roo.Element} this
9233          */
9234         fitToParent : function(monitorResize, targetParent) {
9235           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9236           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9237           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9238             return;
9239           }
9240           var p = Roo.get(targetParent || this.dom.parentNode);
9241           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9242           if (monitorResize === true) {
9243             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9244             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9245           }
9246           return this;
9247         },
9248
9249         /**
9250          * Gets the next sibling, skipping text nodes
9251          * @return {HTMLElement} The next sibling or null
9252          */
9253         getNextSibling : function(){
9254             var n = this.dom.nextSibling;
9255             while(n && n.nodeType != 1){
9256                 n = n.nextSibling;
9257             }
9258             return n;
9259         },
9260
9261         /**
9262          * Gets the previous sibling, skipping text nodes
9263          * @return {HTMLElement} The previous sibling or null
9264          */
9265         getPrevSibling : function(){
9266             var n = this.dom.previousSibling;
9267             while(n && n.nodeType != 1){
9268                 n = n.previousSibling;
9269             }
9270             return n;
9271         },
9272
9273
9274         /**
9275          * Appends the passed element(s) to this element
9276          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9277          * @return {Roo.Element} this
9278          */
9279         appendChild: function(el){
9280             el = Roo.get(el);
9281             el.appendTo(this);
9282             return this;
9283         },
9284
9285         /**
9286          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9287          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9288          * automatically generated with the specified attributes.
9289          * @param {HTMLElement} insertBefore (optional) a child element of this element
9290          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9291          * @return {Roo.Element} The new child element
9292          */
9293         createChild: function(config, insertBefore, returnDom){
9294             config = config || {tag:'div'};
9295             if(insertBefore){
9296                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9297             }
9298             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9299         },
9300
9301         /**
9302          * Appends this element to the passed element
9303          * @param {String/HTMLElement/Element} el The new parent element
9304          * @return {Roo.Element} this
9305          */
9306         appendTo: function(el){
9307             el = Roo.getDom(el);
9308             el.appendChild(this.dom);
9309             return this;
9310         },
9311
9312         /**
9313          * Inserts this element before the passed element in the DOM
9314          * @param {String/HTMLElement/Element} el The element to insert before
9315          * @return {Roo.Element} this
9316          */
9317         insertBefore: function(el){
9318             el = Roo.getDom(el);
9319             el.parentNode.insertBefore(this.dom, el);
9320             return this;
9321         },
9322
9323         /**
9324          * Inserts this element after the passed element in the DOM
9325          * @param {String/HTMLElement/Element} el The element to insert after
9326          * @return {Roo.Element} this
9327          */
9328         insertAfter: function(el){
9329             el = Roo.getDom(el);
9330             el.parentNode.insertBefore(this.dom, el.nextSibling);
9331             return this;
9332         },
9333
9334         /**
9335          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9336          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9337          * @return {Roo.Element} The new child
9338          */
9339         insertFirst: function(el, returnDom){
9340             el = el || {};
9341             if(typeof el == 'object' && !el.nodeType){ // dh config
9342                 return this.createChild(el, this.dom.firstChild, returnDom);
9343             }else{
9344                 el = Roo.getDom(el);
9345                 this.dom.insertBefore(el, this.dom.firstChild);
9346                 return !returnDom ? Roo.get(el) : el;
9347             }
9348         },
9349
9350         /**
9351          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9352          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9353          * @param {String} where (optional) 'before' or 'after' defaults to before
9354          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9355          * @return {Roo.Element} the inserted Element
9356          */
9357         insertSibling: function(el, where, returnDom){
9358             where = where ? where.toLowerCase() : 'before';
9359             el = el || {};
9360             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9361
9362             if(typeof el == 'object' && !el.nodeType){ // dh config
9363                 if(where == 'after' && !this.dom.nextSibling){
9364                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9365                 }else{
9366                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9367                 }
9368
9369             }else{
9370                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9371                             where == 'before' ? this.dom : this.dom.nextSibling);
9372                 if(!returnDom){
9373                     rt = Roo.get(rt);
9374                 }
9375             }
9376             return rt;
9377         },
9378
9379         /**
9380          * Creates and wraps this element with another element
9381          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9382          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9383          * @return {HTMLElement/Element} The newly created wrapper element
9384          */
9385         wrap: function(config, returnDom){
9386             if(!config){
9387                 config = {tag: "div"};
9388             }
9389             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9390             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9391             return newEl;
9392         },
9393
9394         /**
9395          * Replaces the passed element with this element
9396          * @param {String/HTMLElement/Element} el The element to replace
9397          * @return {Roo.Element} this
9398          */
9399         replace: function(el){
9400             el = Roo.get(el);
9401             this.insertBefore(el);
9402             el.remove();
9403             return this;
9404         },
9405
9406         /**
9407          * Inserts an html fragment into this element
9408          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9409          * @param {String} html The HTML fragment
9410          * @param {Boolean} returnEl True to return an Roo.Element
9411          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9412          */
9413         insertHtml : function(where, html, returnEl){
9414             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9415             return returnEl ? Roo.get(el) : el;
9416         },
9417
9418         /**
9419          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9420          * @param {Object} o The object with the attributes
9421          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9422          * @return {Roo.Element} this
9423          */
9424         set : function(o, useSet){
9425             var el = this.dom;
9426             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9427             for(var attr in o){
9428                 if(attr == "style" || typeof o[attr] == "function") continue;
9429                 if(attr=="cls"){
9430                     el.className = o["cls"];
9431                 }else{
9432                     if(useSet) el.setAttribute(attr, o[attr]);
9433                     else el[attr] = o[attr];
9434                 }
9435             }
9436             if(o.style){
9437                 Roo.DomHelper.applyStyles(el, o.style);
9438             }
9439             return this;
9440         },
9441
9442         /**
9443          * Convenience method for constructing a KeyMap
9444          * @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:
9445          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9446          * @param {Function} fn The function to call
9447          * @param {Object} scope (optional) The scope of the function
9448          * @return {Roo.KeyMap} The KeyMap created
9449          */
9450         addKeyListener : function(key, fn, scope){
9451             var config;
9452             if(typeof key != "object" || key instanceof Array){
9453                 config = {
9454                     key: key,
9455                     fn: fn,
9456                     scope: scope
9457                 };
9458             }else{
9459                 config = {
9460                     key : key.key,
9461                     shift : key.shift,
9462                     ctrl : key.ctrl,
9463                     alt : key.alt,
9464                     fn: fn,
9465                     scope: scope
9466                 };
9467             }
9468             return new Roo.KeyMap(this, config);
9469         },
9470
9471         /**
9472          * Creates a KeyMap for this element
9473          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9474          * @return {Roo.KeyMap} The KeyMap created
9475          */
9476         addKeyMap : function(config){
9477             return new Roo.KeyMap(this, config);
9478         },
9479
9480         /**
9481          * Returns true if this element is scrollable.
9482          * @return {Boolean}
9483          */
9484          isScrollable : function(){
9485             var dom = this.dom;
9486             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9487         },
9488
9489         /**
9490          * 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().
9491          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9492          * @param {Number} value The new scroll value
9493          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9494          * @return {Element} this
9495          */
9496
9497         scrollTo : function(side, value, animate){
9498             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9499             if(!animate || !A){
9500                 this.dom[prop] = value;
9501             }else{
9502                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9503                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9504             }
9505             return this;
9506         },
9507
9508         /**
9509          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9510          * within this element's scrollable range.
9511          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9512          * @param {Number} distance How far to scroll the element in pixels
9513          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9514          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9515          * was scrolled as far as it could go.
9516          */
9517          scroll : function(direction, distance, animate){
9518              if(!this.isScrollable()){
9519                  return;
9520              }
9521              var el = this.dom;
9522              var l = el.scrollLeft, t = el.scrollTop;
9523              var w = el.scrollWidth, h = el.scrollHeight;
9524              var cw = el.clientWidth, ch = el.clientHeight;
9525              direction = direction.toLowerCase();
9526              var scrolled = false;
9527              var a = this.preanim(arguments, 2);
9528              switch(direction){
9529                  case "l":
9530                  case "left":
9531                      if(w - l > cw){
9532                          var v = Math.min(l + distance, w-cw);
9533                          this.scrollTo("left", v, a);
9534                          scrolled = true;
9535                      }
9536                      break;
9537                 case "r":
9538                 case "right":
9539                      if(l > 0){
9540                          var v = Math.max(l - distance, 0);
9541                          this.scrollTo("left", v, a);
9542                          scrolled = true;
9543                      }
9544                      break;
9545                 case "t":
9546                 case "top":
9547                 case "up":
9548                      if(t > 0){
9549                          var v = Math.max(t - distance, 0);
9550                          this.scrollTo("top", v, a);
9551                          scrolled = true;
9552                      }
9553                      break;
9554                 case "b":
9555                 case "bottom":
9556                 case "down":
9557                      if(h - t > ch){
9558                          var v = Math.min(t + distance, h-ch);
9559                          this.scrollTo("top", v, a);
9560                          scrolled = true;
9561                      }
9562                      break;
9563              }
9564              return scrolled;
9565         },
9566
9567         /**
9568          * Translates the passed page coordinates into left/top css values for this element
9569          * @param {Number/Array} x The page x or an array containing [x, y]
9570          * @param {Number} y The page y
9571          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9572          */
9573         translatePoints : function(x, y){
9574             if(typeof x == 'object' || x instanceof Array){
9575                 y = x[1]; x = x[0];
9576             }
9577             var p = this.getStyle('position');
9578             var o = this.getXY();
9579
9580             var l = parseInt(this.getStyle('left'), 10);
9581             var t = parseInt(this.getStyle('top'), 10);
9582
9583             if(isNaN(l)){
9584                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9585             }
9586             if(isNaN(t)){
9587                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9588             }
9589
9590             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9591         },
9592
9593         /**
9594          * Returns the current scroll position of the element.
9595          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9596          */
9597         getScroll : function(){
9598             var d = this.dom, doc = document;
9599             if(d == doc || d == doc.body){
9600                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9601                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9602                 return {left: l, top: t};
9603             }else{
9604                 return {left: d.scrollLeft, top: d.scrollTop};
9605             }
9606         },
9607
9608         /**
9609          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9610          * are convert to standard 6 digit hex color.
9611          * @param {String} attr The css attribute
9612          * @param {String} defaultValue The default value to use when a valid color isn't found
9613          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9614          * YUI color anims.
9615          */
9616         getColor : function(attr, defaultValue, prefix){
9617             var v = this.getStyle(attr);
9618             if(!v || v == "transparent" || v == "inherit") {
9619                 return defaultValue;
9620             }
9621             var color = typeof prefix == "undefined" ? "#" : prefix;
9622             if(v.substr(0, 4) == "rgb("){
9623                 var rvs = v.slice(4, v.length -1).split(",");
9624                 for(var i = 0; i < 3; i++){
9625                     var h = parseInt(rvs[i]).toString(16);
9626                     if(h < 16){
9627                         h = "0" + h;
9628                     }
9629                     color += h;
9630                 }
9631             } else {
9632                 if(v.substr(0, 1) == "#"){
9633                     if(v.length == 4) {
9634                         for(var i = 1; i < 4; i++){
9635                             var c = v.charAt(i);
9636                             color +=  c + c;
9637                         }
9638                     }else if(v.length == 7){
9639                         color += v.substr(1);
9640                     }
9641                 }
9642             }
9643             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9644         },
9645
9646         /**
9647          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9648          * gradient background, rounded corners and a 4-way shadow.
9649          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9650          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9651          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9652          * @return {Roo.Element} this
9653          */
9654         boxWrap : function(cls){
9655             cls = cls || 'x-box';
9656             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9657             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9658             return el;
9659         },
9660
9661         /**
9662          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9663          * @param {String} namespace The namespace in which to look for the attribute
9664          * @param {String} name The attribute name
9665          * @return {String} The attribute value
9666          */
9667         getAttributeNS : Roo.isIE ? function(ns, name){
9668             var d = this.dom;
9669             var type = typeof d[ns+":"+name];
9670             if(type != 'undefined' && type != 'unknown'){
9671                 return d[ns+":"+name];
9672             }
9673             return d[name];
9674         } : function(ns, name){
9675             var d = this.dom;
9676             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9677         },
9678         
9679         
9680         /**
9681          * Sets or Returns the value the dom attribute value
9682          * @param {String} name The attribute name
9683          * @param {String} value (optional) The value to set the attribute to
9684          * @return {String} The attribute value
9685          */
9686         attr : function(name){
9687             if (arguments.length > 1) {
9688                 this.dom.setAttribute(name, arguments[1]);
9689                 return arguments[1];
9690             }
9691             if (!this.dom.hasAttribute(name)) {
9692                 return undefined;
9693             }
9694             return this.dom.getAttribute(name);
9695         }
9696         
9697         
9698         
9699     };
9700
9701     var ep = El.prototype;
9702
9703     /**
9704      * Appends an event handler (Shorthand for addListener)
9705      * @param {String}   eventName     The type of event to append
9706      * @param {Function} fn        The method the event invokes
9707      * @param {Object} scope       (optional) The scope (this object) of the fn
9708      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9709      * @method
9710      */
9711     ep.on = ep.addListener;
9712         // backwards compat
9713     ep.mon = ep.addListener;
9714
9715     /**
9716      * Removes an event handler from this element (shorthand for removeListener)
9717      * @param {String} eventName the type of event to remove
9718      * @param {Function} fn the method the event invokes
9719      * @return {Roo.Element} this
9720      * @method
9721      */
9722     ep.un = ep.removeListener;
9723
9724     /**
9725      * true to automatically adjust width and height settings for box-model issues (default to true)
9726      */
9727     ep.autoBoxAdjust = true;
9728
9729     // private
9730     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9731
9732     // private
9733     El.addUnits = function(v, defaultUnit){
9734         if(v === "" || v == "auto"){
9735             return v;
9736         }
9737         if(v === undefined){
9738             return '';
9739         }
9740         if(typeof v == "number" || !El.unitPattern.test(v)){
9741             return v + (defaultUnit || 'px');
9742         }
9743         return v;
9744     };
9745
9746     // special markup used throughout Roo when box wrapping elements
9747     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>';
9748     /**
9749      * Visibility mode constant - Use visibility to hide element
9750      * @static
9751      * @type Number
9752      */
9753     El.VISIBILITY = 1;
9754     /**
9755      * Visibility mode constant - Use display to hide element
9756      * @static
9757      * @type Number
9758      */
9759     El.DISPLAY = 2;
9760
9761     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9762     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9763     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9764
9765
9766
9767     /**
9768      * @private
9769      */
9770     El.cache = {};
9771
9772     var docEl;
9773
9774     /**
9775      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9776      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9777      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9778      * @return {Element} The Element object
9779      * @static
9780      */
9781     El.get = function(el){
9782         var ex, elm, id;
9783         if(!el){ return null; }
9784         if(typeof el == "string"){ // element id
9785             if(!(elm = document.getElementById(el))){
9786                 return null;
9787             }
9788             if(ex = El.cache[el]){
9789                 ex.dom = elm;
9790             }else{
9791                 ex = El.cache[el] = new El(elm);
9792             }
9793             return ex;
9794         }else if(el.tagName){ // dom element
9795             if(!(id = el.id)){
9796                 id = Roo.id(el);
9797             }
9798             if(ex = El.cache[id]){
9799                 ex.dom = el;
9800             }else{
9801                 ex = El.cache[id] = new El(el);
9802             }
9803             return ex;
9804         }else if(el instanceof El){
9805             if(el != docEl){
9806                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9807                                                               // catch case where it hasn't been appended
9808                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9809             }
9810             return el;
9811         }else if(el.isComposite){
9812             return el;
9813         }else if(el instanceof Array){
9814             return El.select(el);
9815         }else if(el == document){
9816             // create a bogus element object representing the document object
9817             if(!docEl){
9818                 var f = function(){};
9819                 f.prototype = El.prototype;
9820                 docEl = new f();
9821                 docEl.dom = document;
9822             }
9823             return docEl;
9824         }
9825         return null;
9826     };
9827
9828     // private
9829     El.uncache = function(el){
9830         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9831             if(a[i]){
9832                 delete El.cache[a[i].id || a[i]];
9833             }
9834         }
9835     };
9836
9837     // private
9838     // Garbage collection - uncache elements/purge listeners on orphaned elements
9839     // so we don't hold a reference and cause the browser to retain them
9840     El.garbageCollect = function(){
9841         if(!Roo.enableGarbageCollector){
9842             clearInterval(El.collectorThread);
9843             return;
9844         }
9845         for(var eid in El.cache){
9846             var el = El.cache[eid], d = el.dom;
9847             // -------------------------------------------------------
9848             // Determining what is garbage:
9849             // -------------------------------------------------------
9850             // !d
9851             // dom node is null, definitely garbage
9852             // -------------------------------------------------------
9853             // !d.parentNode
9854             // no parentNode == direct orphan, definitely garbage
9855             // -------------------------------------------------------
9856             // !d.offsetParent && !document.getElementById(eid)
9857             // display none elements have no offsetParent so we will
9858             // also try to look it up by it's id. However, check
9859             // offsetParent first so we don't do unneeded lookups.
9860             // This enables collection of elements that are not orphans
9861             // directly, but somewhere up the line they have an orphan
9862             // parent.
9863             // -------------------------------------------------------
9864             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9865                 delete El.cache[eid];
9866                 if(d && Roo.enableListenerCollection){
9867                     E.purgeElement(d);
9868                 }
9869             }
9870         }
9871     }
9872     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9873
9874
9875     // dom is optional
9876     El.Flyweight = function(dom){
9877         this.dom = dom;
9878     };
9879     El.Flyweight.prototype = El.prototype;
9880
9881     El._flyweights = {};
9882     /**
9883      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9884      * the dom node can be overwritten by other code.
9885      * @param {String/HTMLElement} el The dom node or id
9886      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9887      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9888      * @static
9889      * @return {Element} The shared Element object
9890      */
9891     El.fly = function(el, named){
9892         named = named || '_global';
9893         el = Roo.getDom(el);
9894         if(!el){
9895             return null;
9896         }
9897         if(!El._flyweights[named]){
9898             El._flyweights[named] = new El.Flyweight();
9899         }
9900         El._flyweights[named].dom = el;
9901         return El._flyweights[named];
9902     };
9903
9904     /**
9905      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9906      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9907      * Shorthand of {@link Roo.Element#get}
9908      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9909      * @return {Element} The Element object
9910      * @member Roo
9911      * @method get
9912      */
9913     Roo.get = El.get;
9914     /**
9915      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9916      * the dom node can be overwritten by other code.
9917      * Shorthand of {@link Roo.Element#fly}
9918      * @param {String/HTMLElement} el The dom node or id
9919      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9920      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9921      * @static
9922      * @return {Element} The shared Element object
9923      * @member Roo
9924      * @method fly
9925      */
9926     Roo.fly = El.fly;
9927
9928     // speedy lookup for elements never to box adjust
9929     var noBoxAdjust = Roo.isStrict ? {
9930         select:1
9931     } : {
9932         input:1, select:1, textarea:1
9933     };
9934     if(Roo.isIE || Roo.isGecko){
9935         noBoxAdjust['button'] = 1;
9936     }
9937
9938
9939     Roo.EventManager.on(window, 'unload', function(){
9940         delete El.cache;
9941         delete El._flyweights;
9942     });
9943 })();
9944
9945
9946
9947
9948 if(Roo.DomQuery){
9949     Roo.Element.selectorFunction = Roo.DomQuery.select;
9950 }
9951
9952 Roo.Element.select = function(selector, unique, root){
9953     var els;
9954     if(typeof selector == "string"){
9955         els = Roo.Element.selectorFunction(selector, root);
9956     }else if(selector.length !== undefined){
9957         els = selector;
9958     }else{
9959         throw "Invalid selector";
9960     }
9961     if(unique === true){
9962         return new Roo.CompositeElement(els);
9963     }else{
9964         return new Roo.CompositeElementLite(els);
9965     }
9966 };
9967 /**
9968  * Selects elements based on the passed CSS selector to enable working on them as 1.
9969  * @param {String/Array} selector The CSS selector or an array of elements
9970  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9971  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9972  * @return {CompositeElementLite/CompositeElement}
9973  * @member Roo
9974  * @method select
9975  */
9976 Roo.select = Roo.Element.select;
9977
9978
9979
9980
9981
9982
9983
9984
9985
9986
9987
9988
9989
9990
9991 /*
9992  * Based on:
9993  * Ext JS Library 1.1.1
9994  * Copyright(c) 2006-2007, Ext JS, LLC.
9995  *
9996  * Originally Released Under LGPL - original licence link has changed is not relivant.
9997  *
9998  * Fork - LGPL
9999  * <script type="text/javascript">
10000  */
10001
10002
10003
10004 //Notifies Element that fx methods are available
10005 Roo.enableFx = true;
10006
10007 /**
10008  * @class Roo.Fx
10009  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10010  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10011  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10012  * Element effects to work.</p><br/>
10013  *
10014  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10015  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10016  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10017  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10018  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10019  * expected results and should be done with care.</p><br/>
10020  *
10021  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10022  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10023 <pre>
10024 Value  Description
10025 -----  -----------------------------
10026 tl     The top left corner
10027 t      The center of the top edge
10028 tr     The top right corner
10029 l      The center of the left edge
10030 r      The center of the right edge
10031 bl     The bottom left corner
10032 b      The center of the bottom edge
10033 br     The bottom right corner
10034 </pre>
10035  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10036  * below are common options that can be passed to any Fx method.</b>
10037  * @cfg {Function} callback A function called when the effect is finished
10038  * @cfg {Object} scope The scope of the effect function
10039  * @cfg {String} easing A valid Easing value for the effect
10040  * @cfg {String} afterCls A css class to apply after the effect
10041  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10042  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10043  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10044  * effects that end with the element being visually hidden, ignored otherwise)
10045  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10046  * a function which returns such a specification that will be applied to the Element after the effect finishes
10047  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10048  * @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
10049  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10050  */
10051 Roo.Fx = {
10052         /**
10053          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10054          * origin for the slide effect.  This function automatically handles wrapping the element with
10055          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10056          * Usage:
10057          *<pre><code>
10058 // default: slide the element in from the top
10059 el.slideIn();
10060
10061 // custom: slide the element in from the right with a 2-second duration
10062 el.slideIn('r', { duration: 2 });
10063
10064 // common config options shown with default values
10065 el.slideIn('t', {
10066     easing: 'easeOut',
10067     duration: .5
10068 });
10069 </code></pre>
10070          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10071          * @param {Object} options (optional) Object literal with any of the Fx config options
10072          * @return {Roo.Element} The Element
10073          */
10074     slideIn : function(anchor, o){
10075         var el = this.getFxEl();
10076         o = o || {};
10077
10078         el.queueFx(o, function(){
10079
10080             anchor = anchor || "t";
10081
10082             // fix display to visibility
10083             this.fixDisplay();
10084
10085             // restore values after effect
10086             var r = this.getFxRestore();
10087             var b = this.getBox();
10088             // fixed size for slide
10089             this.setSize(b);
10090
10091             // wrap if needed
10092             var wrap = this.fxWrap(r.pos, o, "hidden");
10093
10094             var st = this.dom.style;
10095             st.visibility = "visible";
10096             st.position = "absolute";
10097
10098             // clear out temp styles after slide and unwrap
10099             var after = function(){
10100                 el.fxUnwrap(wrap, r.pos, o);
10101                 st.width = r.width;
10102                 st.height = r.height;
10103                 el.afterFx(o);
10104             };
10105             // time to calc the positions
10106             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10107
10108             switch(anchor.toLowerCase()){
10109                 case "t":
10110                     wrap.setSize(b.width, 0);
10111                     st.left = st.bottom = "0";
10112                     a = {height: bh};
10113                 break;
10114                 case "l":
10115                     wrap.setSize(0, b.height);
10116                     st.right = st.top = "0";
10117                     a = {width: bw};
10118                 break;
10119                 case "r":
10120                     wrap.setSize(0, b.height);
10121                     wrap.setX(b.right);
10122                     st.left = st.top = "0";
10123                     a = {width: bw, points: pt};
10124                 break;
10125                 case "b":
10126                     wrap.setSize(b.width, 0);
10127                     wrap.setY(b.bottom);
10128                     st.left = st.top = "0";
10129                     a = {height: bh, points: pt};
10130                 break;
10131                 case "tl":
10132                     wrap.setSize(0, 0);
10133                     st.right = st.bottom = "0";
10134                     a = {width: bw, height: bh};
10135                 break;
10136                 case "bl":
10137                     wrap.setSize(0, 0);
10138                     wrap.setY(b.y+b.height);
10139                     st.right = st.top = "0";
10140                     a = {width: bw, height: bh, points: pt};
10141                 break;
10142                 case "br":
10143                     wrap.setSize(0, 0);
10144                     wrap.setXY([b.right, b.bottom]);
10145                     st.left = st.top = "0";
10146                     a = {width: bw, height: bh, points: pt};
10147                 break;
10148                 case "tr":
10149                     wrap.setSize(0, 0);
10150                     wrap.setX(b.x+b.width);
10151                     st.left = st.bottom = "0";
10152                     a = {width: bw, height: bh, points: pt};
10153                 break;
10154             }
10155             this.dom.style.visibility = "visible";
10156             wrap.show();
10157
10158             arguments.callee.anim = wrap.fxanim(a,
10159                 o,
10160                 'motion',
10161                 .5,
10162                 'easeOut', after);
10163         });
10164         return this;
10165     },
10166     
10167         /**
10168          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10169          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10170          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10171          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10172          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10173          * Usage:
10174          *<pre><code>
10175 // default: slide the element out to the top
10176 el.slideOut();
10177
10178 // custom: slide the element out to the right with a 2-second duration
10179 el.slideOut('r', { duration: 2 });
10180
10181 // common config options shown with default values
10182 el.slideOut('t', {
10183     easing: 'easeOut',
10184     duration: .5,
10185     remove: false,
10186     useDisplay: false
10187 });
10188 </code></pre>
10189          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10190          * @param {Object} options (optional) Object literal with any of the Fx config options
10191          * @return {Roo.Element} The Element
10192          */
10193     slideOut : function(anchor, o){
10194         var el = this.getFxEl();
10195         o = o || {};
10196
10197         el.queueFx(o, function(){
10198
10199             anchor = anchor || "t";
10200
10201             // restore values after effect
10202             var r = this.getFxRestore();
10203             
10204             var b = this.getBox();
10205             // fixed size for slide
10206             this.setSize(b);
10207
10208             // wrap if needed
10209             var wrap = this.fxWrap(r.pos, o, "visible");
10210
10211             var st = this.dom.style;
10212             st.visibility = "visible";
10213             st.position = "absolute";
10214
10215             wrap.setSize(b);
10216
10217             var after = function(){
10218                 if(o.useDisplay){
10219                     el.setDisplayed(false);
10220                 }else{
10221                     el.hide();
10222                 }
10223
10224                 el.fxUnwrap(wrap, r.pos, o);
10225
10226                 st.width = r.width;
10227                 st.height = r.height;
10228
10229                 el.afterFx(o);
10230             };
10231
10232             var a, zero = {to: 0};
10233             switch(anchor.toLowerCase()){
10234                 case "t":
10235                     st.left = st.bottom = "0";
10236                     a = {height: zero};
10237                 break;
10238                 case "l":
10239                     st.right = st.top = "0";
10240                     a = {width: zero};
10241                 break;
10242                 case "r":
10243                     st.left = st.top = "0";
10244                     a = {width: zero, points: {to:[b.right, b.y]}};
10245                 break;
10246                 case "b":
10247                     st.left = st.top = "0";
10248                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10249                 break;
10250                 case "tl":
10251                     st.right = st.bottom = "0";
10252                     a = {width: zero, height: zero};
10253                 break;
10254                 case "bl":
10255                     st.right = st.top = "0";
10256                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10257                 break;
10258                 case "br":
10259                     st.left = st.top = "0";
10260                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10261                 break;
10262                 case "tr":
10263                     st.left = st.bottom = "0";
10264                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10265                 break;
10266             }
10267
10268             arguments.callee.anim = wrap.fxanim(a,
10269                 o,
10270                 'motion',
10271                 .5,
10272                 "easeOut", after);
10273         });
10274         return this;
10275     },
10276
10277         /**
10278          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10279          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10280          * The element must be removed from the DOM using the 'remove' config option if desired.
10281          * Usage:
10282          *<pre><code>
10283 // default
10284 el.puff();
10285
10286 // common config options shown with default values
10287 el.puff({
10288     easing: 'easeOut',
10289     duration: .5,
10290     remove: false,
10291     useDisplay: false
10292 });
10293 </code></pre>
10294          * @param {Object} options (optional) Object literal with any of the Fx config options
10295          * @return {Roo.Element} The Element
10296          */
10297     puff : function(o){
10298         var el = this.getFxEl();
10299         o = o || {};
10300
10301         el.queueFx(o, function(){
10302             this.clearOpacity();
10303             this.show();
10304
10305             // restore values after effect
10306             var r = this.getFxRestore();
10307             var st = this.dom.style;
10308
10309             var after = function(){
10310                 if(o.useDisplay){
10311                     el.setDisplayed(false);
10312                 }else{
10313                     el.hide();
10314                 }
10315
10316                 el.clearOpacity();
10317
10318                 el.setPositioning(r.pos);
10319                 st.width = r.width;
10320                 st.height = r.height;
10321                 st.fontSize = '';
10322                 el.afterFx(o);
10323             };
10324
10325             var width = this.getWidth();
10326             var height = this.getHeight();
10327
10328             arguments.callee.anim = this.fxanim({
10329                     width : {to: this.adjustWidth(width * 2)},
10330                     height : {to: this.adjustHeight(height * 2)},
10331                     points : {by: [-(width * .5), -(height * .5)]},
10332                     opacity : {to: 0},
10333                     fontSize: {to:200, unit: "%"}
10334                 },
10335                 o,
10336                 'motion',
10337                 .5,
10338                 "easeOut", after);
10339         });
10340         return this;
10341     },
10342
10343         /**
10344          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10345          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10346          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10347          * Usage:
10348          *<pre><code>
10349 // default
10350 el.switchOff();
10351
10352 // all config options shown with default values
10353 el.switchOff({
10354     easing: 'easeIn',
10355     duration: .3,
10356     remove: false,
10357     useDisplay: false
10358 });
10359 </code></pre>
10360          * @param {Object} options (optional) Object literal with any of the Fx config options
10361          * @return {Roo.Element} The Element
10362          */
10363     switchOff : function(o){
10364         var el = this.getFxEl();
10365         o = o || {};
10366
10367         el.queueFx(o, function(){
10368             this.clearOpacity();
10369             this.clip();
10370
10371             // restore values after effect
10372             var r = this.getFxRestore();
10373             var st = this.dom.style;
10374
10375             var after = function(){
10376                 if(o.useDisplay){
10377                     el.setDisplayed(false);
10378                 }else{
10379                     el.hide();
10380                 }
10381
10382                 el.clearOpacity();
10383                 el.setPositioning(r.pos);
10384                 st.width = r.width;
10385                 st.height = r.height;
10386
10387                 el.afterFx(o);
10388             };
10389
10390             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10391                 this.clearOpacity();
10392                 (function(){
10393                     this.fxanim({
10394                         height:{to:1},
10395                         points:{by:[0, this.getHeight() * .5]}
10396                     }, o, 'motion', 0.3, 'easeIn', after);
10397                 }).defer(100, this);
10398             });
10399         });
10400         return this;
10401     },
10402
10403     /**
10404      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10405      * changed using the "attr" config option) and then fading back to the original color. If no original
10406      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10407      * Usage:
10408 <pre><code>
10409 // default: highlight background to yellow
10410 el.highlight();
10411
10412 // custom: highlight foreground text to blue for 2 seconds
10413 el.highlight("0000ff", { attr: 'color', duration: 2 });
10414
10415 // common config options shown with default values
10416 el.highlight("ffff9c", {
10417     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10418     endColor: (current color) or "ffffff",
10419     easing: 'easeIn',
10420     duration: 1
10421 });
10422 </code></pre>
10423      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10424      * @param {Object} options (optional) Object literal with any of the Fx config options
10425      * @return {Roo.Element} The Element
10426      */ 
10427     highlight : function(color, o){
10428         var el = this.getFxEl();
10429         o = o || {};
10430
10431         el.queueFx(o, function(){
10432             color = color || "ffff9c";
10433             attr = o.attr || "backgroundColor";
10434
10435             this.clearOpacity();
10436             this.show();
10437
10438             var origColor = this.getColor(attr);
10439             var restoreColor = this.dom.style[attr];
10440             endColor = (o.endColor || origColor) || "ffffff";
10441
10442             var after = function(){
10443                 el.dom.style[attr] = restoreColor;
10444                 el.afterFx(o);
10445             };
10446
10447             var a = {};
10448             a[attr] = {from: color, to: endColor};
10449             arguments.callee.anim = this.fxanim(a,
10450                 o,
10451                 'color',
10452                 1,
10453                 'easeIn', after);
10454         });
10455         return this;
10456     },
10457
10458    /**
10459     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10460     * Usage:
10461 <pre><code>
10462 // default: a single light blue ripple
10463 el.frame();
10464
10465 // custom: 3 red ripples lasting 3 seconds total
10466 el.frame("ff0000", 3, { duration: 3 });
10467
10468 // common config options shown with default values
10469 el.frame("C3DAF9", 1, {
10470     duration: 1 //duration of entire animation (not each individual ripple)
10471     // Note: Easing is not configurable and will be ignored if included
10472 });
10473 </code></pre>
10474     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10475     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10476     * @param {Object} options (optional) Object literal with any of the Fx config options
10477     * @return {Roo.Element} The Element
10478     */
10479     frame : function(color, count, o){
10480         var el = this.getFxEl();
10481         o = o || {};
10482
10483         el.queueFx(o, function(){
10484             color = color || "#C3DAF9";
10485             if(color.length == 6){
10486                 color = "#" + color;
10487             }
10488             count = count || 1;
10489             duration = o.duration || 1;
10490             this.show();
10491
10492             var b = this.getBox();
10493             var animFn = function(){
10494                 var proxy = this.createProxy({
10495
10496                      style:{
10497                         visbility:"hidden",
10498                         position:"absolute",
10499                         "z-index":"35000", // yee haw
10500                         border:"0px solid " + color
10501                      }
10502                   });
10503                 var scale = Roo.isBorderBox ? 2 : 1;
10504                 proxy.animate({
10505                     top:{from:b.y, to:b.y - 20},
10506                     left:{from:b.x, to:b.x - 20},
10507                     borderWidth:{from:0, to:10},
10508                     opacity:{from:1, to:0},
10509                     height:{from:b.height, to:(b.height + (20*scale))},
10510                     width:{from:b.width, to:(b.width + (20*scale))}
10511                 }, duration, function(){
10512                     proxy.remove();
10513                 });
10514                 if(--count > 0){
10515                      animFn.defer((duration/2)*1000, this);
10516                 }else{
10517                     el.afterFx(o);
10518                 }
10519             };
10520             animFn.call(this);
10521         });
10522         return this;
10523     },
10524
10525    /**
10526     * Creates a pause before any subsequent queued effects begin.  If there are
10527     * no effects queued after the pause it will have no effect.
10528     * Usage:
10529 <pre><code>
10530 el.pause(1);
10531 </code></pre>
10532     * @param {Number} seconds The length of time to pause (in seconds)
10533     * @return {Roo.Element} The Element
10534     */
10535     pause : function(seconds){
10536         var el = this.getFxEl();
10537         var o = {};
10538
10539         el.queueFx(o, function(){
10540             setTimeout(function(){
10541                 el.afterFx(o);
10542             }, seconds * 1000);
10543         });
10544         return this;
10545     },
10546
10547    /**
10548     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10549     * using the "endOpacity" config option.
10550     * Usage:
10551 <pre><code>
10552 // default: fade in from opacity 0 to 100%
10553 el.fadeIn();
10554
10555 // custom: fade in from opacity 0 to 75% over 2 seconds
10556 el.fadeIn({ endOpacity: .75, duration: 2});
10557
10558 // common config options shown with default values
10559 el.fadeIn({
10560     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10561     easing: 'easeOut',
10562     duration: .5
10563 });
10564 </code></pre>
10565     * @param {Object} options (optional) Object literal with any of the Fx config options
10566     * @return {Roo.Element} The Element
10567     */
10568     fadeIn : function(o){
10569         var el = this.getFxEl();
10570         o = o || {};
10571         el.queueFx(o, function(){
10572             this.setOpacity(0);
10573             this.fixDisplay();
10574             this.dom.style.visibility = 'visible';
10575             var to = o.endOpacity || 1;
10576             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10577                 o, null, .5, "easeOut", function(){
10578                 if(to == 1){
10579                     this.clearOpacity();
10580                 }
10581                 el.afterFx(o);
10582             });
10583         });
10584         return this;
10585     },
10586
10587    /**
10588     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10589     * using the "endOpacity" config option.
10590     * Usage:
10591 <pre><code>
10592 // default: fade out from the element's current opacity to 0
10593 el.fadeOut();
10594
10595 // custom: fade out from the element's current opacity to 25% over 2 seconds
10596 el.fadeOut({ endOpacity: .25, duration: 2});
10597
10598 // common config options shown with default values
10599 el.fadeOut({
10600     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10601     easing: 'easeOut',
10602     duration: .5
10603     remove: false,
10604     useDisplay: false
10605 });
10606 </code></pre>
10607     * @param {Object} options (optional) Object literal with any of the Fx config options
10608     * @return {Roo.Element} The Element
10609     */
10610     fadeOut : function(o){
10611         var el = this.getFxEl();
10612         o = o || {};
10613         el.queueFx(o, function(){
10614             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10615                 o, null, .5, "easeOut", function(){
10616                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10617                      this.dom.style.display = "none";
10618                 }else{
10619                      this.dom.style.visibility = "hidden";
10620                 }
10621                 this.clearOpacity();
10622                 el.afterFx(o);
10623             });
10624         });
10625         return this;
10626     },
10627
10628    /**
10629     * Animates the transition of an element's dimensions from a starting height/width
10630     * to an ending height/width.
10631     * Usage:
10632 <pre><code>
10633 // change height and width to 100x100 pixels
10634 el.scale(100, 100);
10635
10636 // common config options shown with default values.  The height and width will default to
10637 // the element's existing values if passed as null.
10638 el.scale(
10639     [element's width],
10640     [element's height], {
10641     easing: 'easeOut',
10642     duration: .35
10643 });
10644 </code></pre>
10645     * @param {Number} width  The new width (pass undefined to keep the original width)
10646     * @param {Number} height  The new height (pass undefined to keep the original height)
10647     * @param {Object} options (optional) Object literal with any of the Fx config options
10648     * @return {Roo.Element} The Element
10649     */
10650     scale : function(w, h, o){
10651         this.shift(Roo.apply({}, o, {
10652             width: w,
10653             height: h
10654         }));
10655         return this;
10656     },
10657
10658    /**
10659     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10660     * Any of these properties not specified in the config object will not be changed.  This effect 
10661     * requires that at least one new dimension, position or opacity setting must be passed in on
10662     * the config object in order for the function to have any effect.
10663     * Usage:
10664 <pre><code>
10665 // slide the element horizontally to x position 200 while changing the height and opacity
10666 el.shift({ x: 200, height: 50, opacity: .8 });
10667
10668 // common config options shown with default values.
10669 el.shift({
10670     width: [element's width],
10671     height: [element's height],
10672     x: [element's x position],
10673     y: [element's y position],
10674     opacity: [element's opacity],
10675     easing: 'easeOut',
10676     duration: .35
10677 });
10678 </code></pre>
10679     * @param {Object} options  Object literal with any of the Fx config options
10680     * @return {Roo.Element} The Element
10681     */
10682     shift : function(o){
10683         var el = this.getFxEl();
10684         o = o || {};
10685         el.queueFx(o, function(){
10686             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10687             if(w !== undefined){
10688                 a.width = {to: this.adjustWidth(w)};
10689             }
10690             if(h !== undefined){
10691                 a.height = {to: this.adjustHeight(h)};
10692             }
10693             if(x !== undefined || y !== undefined){
10694                 a.points = {to: [
10695                     x !== undefined ? x : this.getX(),
10696                     y !== undefined ? y : this.getY()
10697                 ]};
10698             }
10699             if(op !== undefined){
10700                 a.opacity = {to: op};
10701             }
10702             if(o.xy !== undefined){
10703                 a.points = {to: o.xy};
10704             }
10705             arguments.callee.anim = this.fxanim(a,
10706                 o, 'motion', .35, "easeOut", function(){
10707                 el.afterFx(o);
10708             });
10709         });
10710         return this;
10711     },
10712
10713         /**
10714          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10715          * ending point of the effect.
10716          * Usage:
10717          *<pre><code>
10718 // default: slide the element downward while fading out
10719 el.ghost();
10720
10721 // custom: slide the element out to the right with a 2-second duration
10722 el.ghost('r', { duration: 2 });
10723
10724 // common config options shown with default values
10725 el.ghost('b', {
10726     easing: 'easeOut',
10727     duration: .5
10728     remove: false,
10729     useDisplay: false
10730 });
10731 </code></pre>
10732          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10733          * @param {Object} options (optional) Object literal with any of the Fx config options
10734          * @return {Roo.Element} The Element
10735          */
10736     ghost : function(anchor, o){
10737         var el = this.getFxEl();
10738         o = o || {};
10739
10740         el.queueFx(o, function(){
10741             anchor = anchor || "b";
10742
10743             // restore values after effect
10744             var r = this.getFxRestore();
10745             var w = this.getWidth(),
10746                 h = this.getHeight();
10747
10748             var st = this.dom.style;
10749
10750             var after = function(){
10751                 if(o.useDisplay){
10752                     el.setDisplayed(false);
10753                 }else{
10754                     el.hide();
10755                 }
10756
10757                 el.clearOpacity();
10758                 el.setPositioning(r.pos);
10759                 st.width = r.width;
10760                 st.height = r.height;
10761
10762                 el.afterFx(o);
10763             };
10764
10765             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10766             switch(anchor.toLowerCase()){
10767                 case "t":
10768                     pt.by = [0, -h];
10769                 break;
10770                 case "l":
10771                     pt.by = [-w, 0];
10772                 break;
10773                 case "r":
10774                     pt.by = [w, 0];
10775                 break;
10776                 case "b":
10777                     pt.by = [0, h];
10778                 break;
10779                 case "tl":
10780                     pt.by = [-w, -h];
10781                 break;
10782                 case "bl":
10783                     pt.by = [-w, h];
10784                 break;
10785                 case "br":
10786                     pt.by = [w, h];
10787                 break;
10788                 case "tr":
10789                     pt.by = [w, -h];
10790                 break;
10791             }
10792
10793             arguments.callee.anim = this.fxanim(a,
10794                 o,
10795                 'motion',
10796                 .5,
10797                 "easeOut", after);
10798         });
10799         return this;
10800     },
10801
10802         /**
10803          * Ensures that all effects queued after syncFx is called on the element are
10804          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10805          * @return {Roo.Element} The Element
10806          */
10807     syncFx : function(){
10808         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10809             block : false,
10810             concurrent : true,
10811             stopFx : false
10812         });
10813         return this;
10814     },
10815
10816         /**
10817          * Ensures that all effects queued after sequenceFx is called on the element are
10818          * run in sequence.  This is the opposite of {@link #syncFx}.
10819          * @return {Roo.Element} The Element
10820          */
10821     sequenceFx : function(){
10822         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10823             block : false,
10824             concurrent : false,
10825             stopFx : false
10826         });
10827         return this;
10828     },
10829
10830         /* @private */
10831     nextFx : function(){
10832         var ef = this.fxQueue[0];
10833         if(ef){
10834             ef.call(this);
10835         }
10836     },
10837
10838         /**
10839          * Returns true if the element has any effects actively running or queued, else returns false.
10840          * @return {Boolean} True if element has active effects, else false
10841          */
10842     hasActiveFx : function(){
10843         return this.fxQueue && this.fxQueue[0];
10844     },
10845
10846         /**
10847          * Stops any running effects and clears the element's internal effects queue if it contains
10848          * any additional effects that haven't started yet.
10849          * @return {Roo.Element} The Element
10850          */
10851     stopFx : function(){
10852         if(this.hasActiveFx()){
10853             var cur = this.fxQueue[0];
10854             if(cur && cur.anim && cur.anim.isAnimated()){
10855                 this.fxQueue = [cur]; // clear out others
10856                 cur.anim.stop(true);
10857             }
10858         }
10859         return this;
10860     },
10861
10862         /* @private */
10863     beforeFx : function(o){
10864         if(this.hasActiveFx() && !o.concurrent){
10865            if(o.stopFx){
10866                this.stopFx();
10867                return true;
10868            }
10869            return false;
10870         }
10871         return true;
10872     },
10873
10874         /**
10875          * Returns true if the element is currently blocking so that no other effect can be queued
10876          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10877          * used to ensure that an effect initiated by a user action runs to completion prior to the
10878          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10879          * @return {Boolean} True if blocking, else false
10880          */
10881     hasFxBlock : function(){
10882         var q = this.fxQueue;
10883         return q && q[0] && q[0].block;
10884     },
10885
10886         /* @private */
10887     queueFx : function(o, fn){
10888         if(!this.fxQueue){
10889             this.fxQueue = [];
10890         }
10891         if(!this.hasFxBlock()){
10892             Roo.applyIf(o, this.fxDefaults);
10893             if(!o.concurrent){
10894                 var run = this.beforeFx(o);
10895                 fn.block = o.block;
10896                 this.fxQueue.push(fn);
10897                 if(run){
10898                     this.nextFx();
10899                 }
10900             }else{
10901                 fn.call(this);
10902             }
10903         }
10904         return this;
10905     },
10906
10907         /* @private */
10908     fxWrap : function(pos, o, vis){
10909         var wrap;
10910         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10911             var wrapXY;
10912             if(o.fixPosition){
10913                 wrapXY = this.getXY();
10914             }
10915             var div = document.createElement("div");
10916             div.style.visibility = vis;
10917             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10918             wrap.setPositioning(pos);
10919             if(wrap.getStyle("position") == "static"){
10920                 wrap.position("relative");
10921             }
10922             this.clearPositioning('auto');
10923             wrap.clip();
10924             wrap.dom.appendChild(this.dom);
10925             if(wrapXY){
10926                 wrap.setXY(wrapXY);
10927             }
10928         }
10929         return wrap;
10930     },
10931
10932         /* @private */
10933     fxUnwrap : function(wrap, pos, o){
10934         this.clearPositioning();
10935         this.setPositioning(pos);
10936         if(!o.wrap){
10937             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10938             wrap.remove();
10939         }
10940     },
10941
10942         /* @private */
10943     getFxRestore : function(){
10944         var st = this.dom.style;
10945         return {pos: this.getPositioning(), width: st.width, height : st.height};
10946     },
10947
10948         /* @private */
10949     afterFx : function(o){
10950         if(o.afterStyle){
10951             this.applyStyles(o.afterStyle);
10952         }
10953         if(o.afterCls){
10954             this.addClass(o.afterCls);
10955         }
10956         if(o.remove === true){
10957             this.remove();
10958         }
10959         Roo.callback(o.callback, o.scope, [this]);
10960         if(!o.concurrent){
10961             this.fxQueue.shift();
10962             this.nextFx();
10963         }
10964     },
10965
10966         /* @private */
10967     getFxEl : function(){ // support for composite element fx
10968         return Roo.get(this.dom);
10969     },
10970
10971         /* @private */
10972     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10973         animType = animType || 'run';
10974         opt = opt || {};
10975         var anim = Roo.lib.Anim[animType](
10976             this.dom, args,
10977             (opt.duration || defaultDur) || .35,
10978             (opt.easing || defaultEase) || 'easeOut',
10979             function(){
10980                 Roo.callback(cb, this);
10981             },
10982             this
10983         );
10984         opt.anim = anim;
10985         return anim;
10986     }
10987 };
10988
10989 // backwords compat
10990 Roo.Fx.resize = Roo.Fx.scale;
10991
10992 //When included, Roo.Fx is automatically applied to Element so that all basic
10993 //effects are available directly via the Element API
10994 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10995  * Based on:
10996  * Ext JS Library 1.1.1
10997  * Copyright(c) 2006-2007, Ext JS, LLC.
10998  *
10999  * Originally Released Under LGPL - original licence link has changed is not relivant.
11000  *
11001  * Fork - LGPL
11002  * <script type="text/javascript">
11003  */
11004
11005
11006 /**
11007  * @class Roo.CompositeElement
11008  * Standard composite class. Creates a Roo.Element for every element in the collection.
11009  * <br><br>
11010  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11011  * actions will be performed on all the elements in this collection.</b>
11012  * <br><br>
11013  * All methods return <i>this</i> and can be chained.
11014  <pre><code>
11015  var els = Roo.select("#some-el div.some-class", true);
11016  // or select directly from an existing element
11017  var el = Roo.get('some-el');
11018  el.select('div.some-class', true);
11019
11020  els.setWidth(100); // all elements become 100 width
11021  els.hide(true); // all elements fade out and hide
11022  // or
11023  els.setWidth(100).hide(true);
11024  </code></pre>
11025  */
11026 Roo.CompositeElement = function(els){
11027     this.elements = [];
11028     this.addElements(els);
11029 };
11030 Roo.CompositeElement.prototype = {
11031     isComposite: true,
11032     addElements : function(els){
11033         if(!els) return this;
11034         if(typeof els == "string"){
11035             els = Roo.Element.selectorFunction(els);
11036         }
11037         var yels = this.elements;
11038         var index = yels.length-1;
11039         for(var i = 0, len = els.length; i < len; i++) {
11040                 yels[++index] = Roo.get(els[i]);
11041         }
11042         return this;
11043     },
11044
11045     /**
11046     * Clears this composite and adds the elements returned by the passed selector.
11047     * @param {String/Array} els A string CSS selector, an array of elements or an element
11048     * @return {CompositeElement} this
11049     */
11050     fill : function(els){
11051         this.elements = [];
11052         this.add(els);
11053         return this;
11054     },
11055
11056     /**
11057     * Filters this composite to only elements that match the passed selector.
11058     * @param {String} selector A string CSS selector
11059     * @return {CompositeElement} this
11060     */
11061     filter : function(selector){
11062         var els = [];
11063         this.each(function(el){
11064             if(el.is(selector)){
11065                 els[els.length] = el.dom;
11066             }
11067         });
11068         this.fill(els);
11069         return this;
11070     },
11071
11072     invoke : function(fn, args){
11073         var els = this.elements;
11074         for(var i = 0, len = els.length; i < len; i++) {
11075                 Roo.Element.prototype[fn].apply(els[i], args);
11076         }
11077         return this;
11078     },
11079     /**
11080     * Adds elements to this composite.
11081     * @param {String/Array} els A string CSS selector, an array of elements or an element
11082     * @return {CompositeElement} this
11083     */
11084     add : function(els){
11085         if(typeof els == "string"){
11086             this.addElements(Roo.Element.selectorFunction(els));
11087         }else if(els.length !== undefined){
11088             this.addElements(els);
11089         }else{
11090             this.addElements([els]);
11091         }
11092         return this;
11093     },
11094     /**
11095     * Calls the passed function passing (el, this, index) for each element in this composite.
11096     * @param {Function} fn The function to call
11097     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11098     * @return {CompositeElement} this
11099     */
11100     each : function(fn, scope){
11101         var els = this.elements;
11102         for(var i = 0, len = els.length; i < len; i++){
11103             if(fn.call(scope || els[i], els[i], this, i) === false) {
11104                 break;
11105             }
11106         }
11107         return this;
11108     },
11109
11110     /**
11111      * Returns the Element object at the specified index
11112      * @param {Number} index
11113      * @return {Roo.Element}
11114      */
11115     item : function(index){
11116         return this.elements[index] || null;
11117     },
11118
11119     /**
11120      * Returns the first Element
11121      * @return {Roo.Element}
11122      */
11123     first : function(){
11124         return this.item(0);
11125     },
11126
11127     /**
11128      * Returns the last Element
11129      * @return {Roo.Element}
11130      */
11131     last : function(){
11132         return this.item(this.elements.length-1);
11133     },
11134
11135     /**
11136      * Returns the number of elements in this composite
11137      * @return Number
11138      */
11139     getCount : function(){
11140         return this.elements.length;
11141     },
11142
11143     /**
11144      * Returns true if this composite contains the passed element
11145      * @return Boolean
11146      */
11147     contains : function(el){
11148         return this.indexOf(el) !== -1;
11149     },
11150
11151     /**
11152      * Returns true if this composite contains the passed element
11153      * @return Boolean
11154      */
11155     indexOf : function(el){
11156         return this.elements.indexOf(Roo.get(el));
11157     },
11158
11159
11160     /**
11161     * Removes the specified element(s).
11162     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11163     * or an array of any of those.
11164     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11165     * @return {CompositeElement} this
11166     */
11167     removeElement : function(el, removeDom){
11168         if(el instanceof Array){
11169             for(var i = 0, len = el.length; i < len; i++){
11170                 this.removeElement(el[i]);
11171             }
11172             return this;
11173         }
11174         var index = typeof el == 'number' ? el : this.indexOf(el);
11175         if(index !== -1){
11176             if(removeDom){
11177                 var d = this.elements[index];
11178                 if(d.dom){
11179                     d.remove();
11180                 }else{
11181                     d.parentNode.removeChild(d);
11182                 }
11183             }
11184             this.elements.splice(index, 1);
11185         }
11186         return this;
11187     },
11188
11189     /**
11190     * Replaces the specified element with the passed element.
11191     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11192     * to replace.
11193     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11194     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11195     * @return {CompositeElement} this
11196     */
11197     replaceElement : function(el, replacement, domReplace){
11198         var index = typeof el == 'number' ? el : this.indexOf(el);
11199         if(index !== -1){
11200             if(domReplace){
11201                 this.elements[index].replaceWith(replacement);
11202             }else{
11203                 this.elements.splice(index, 1, Roo.get(replacement))
11204             }
11205         }
11206         return this;
11207     },
11208
11209     /**
11210      * Removes all elements.
11211      */
11212     clear : function(){
11213         this.elements = [];
11214     }
11215 };
11216 (function(){
11217     Roo.CompositeElement.createCall = function(proto, fnName){
11218         if(!proto[fnName]){
11219             proto[fnName] = function(){
11220                 return this.invoke(fnName, arguments);
11221             };
11222         }
11223     };
11224     for(var fnName in Roo.Element.prototype){
11225         if(typeof Roo.Element.prototype[fnName] == "function"){
11226             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11227         }
11228     };
11229 })();
11230 /*
11231  * Based on:
11232  * Ext JS Library 1.1.1
11233  * Copyright(c) 2006-2007, Ext JS, LLC.
11234  *
11235  * Originally Released Under LGPL - original licence link has changed is not relivant.
11236  *
11237  * Fork - LGPL
11238  * <script type="text/javascript">
11239  */
11240
11241 /**
11242  * @class Roo.CompositeElementLite
11243  * @extends Roo.CompositeElement
11244  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11245  <pre><code>
11246  var els = Roo.select("#some-el div.some-class");
11247  // or select directly from an existing element
11248  var el = Roo.get('some-el');
11249  el.select('div.some-class');
11250
11251  els.setWidth(100); // all elements become 100 width
11252  els.hide(true); // all elements fade out and hide
11253  // or
11254  els.setWidth(100).hide(true);
11255  </code></pre><br><br>
11256  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11257  * actions will be performed on all the elements in this collection.</b>
11258  */
11259 Roo.CompositeElementLite = function(els){
11260     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11261     this.el = new Roo.Element.Flyweight();
11262 };
11263 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11264     addElements : function(els){
11265         if(els){
11266             if(els instanceof Array){
11267                 this.elements = this.elements.concat(els);
11268             }else{
11269                 var yels = this.elements;
11270                 var index = yels.length-1;
11271                 for(var i = 0, len = els.length; i < len; i++) {
11272                     yels[++index] = els[i];
11273                 }
11274             }
11275         }
11276         return this;
11277     },
11278     invoke : function(fn, args){
11279         var els = this.elements;
11280         var el = this.el;
11281         for(var i = 0, len = els.length; i < len; i++) {
11282             el.dom = els[i];
11283                 Roo.Element.prototype[fn].apply(el, args);
11284         }
11285         return this;
11286     },
11287     /**
11288      * Returns a flyweight Element of the dom element object at the specified index
11289      * @param {Number} index
11290      * @return {Roo.Element}
11291      */
11292     item : function(index){
11293         if(!this.elements[index]){
11294             return null;
11295         }
11296         this.el.dom = this.elements[index];
11297         return this.el;
11298     },
11299
11300     // fixes scope with flyweight
11301     addListener : function(eventName, handler, scope, opt){
11302         var els = this.elements;
11303         for(var i = 0, len = els.length; i < len; i++) {
11304             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11305         }
11306         return this;
11307     },
11308
11309     /**
11310     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11311     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11312     * a reference to the dom node, use el.dom.</b>
11313     * @param {Function} fn The function to call
11314     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11315     * @return {CompositeElement} this
11316     */
11317     each : function(fn, scope){
11318         var els = this.elements;
11319         var el = this.el;
11320         for(var i = 0, len = els.length; i < len; i++){
11321             el.dom = els[i];
11322                 if(fn.call(scope || el, el, this, i) === false){
11323                 break;
11324             }
11325         }
11326         return this;
11327     },
11328
11329     indexOf : function(el){
11330         return this.elements.indexOf(Roo.getDom(el));
11331     },
11332
11333     replaceElement : function(el, replacement, domReplace){
11334         var index = typeof el == 'number' ? el : this.indexOf(el);
11335         if(index !== -1){
11336             replacement = Roo.getDom(replacement);
11337             if(domReplace){
11338                 var d = this.elements[index];
11339                 d.parentNode.insertBefore(replacement, d);
11340                 d.parentNode.removeChild(d);
11341             }
11342             this.elements.splice(index, 1, replacement);
11343         }
11344         return this;
11345     }
11346 });
11347 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11348
11349 /*
11350  * Based on:
11351  * Ext JS Library 1.1.1
11352  * Copyright(c) 2006-2007, Ext JS, LLC.
11353  *
11354  * Originally Released Under LGPL - original licence link has changed is not relivant.
11355  *
11356  * Fork - LGPL
11357  * <script type="text/javascript">
11358  */
11359
11360  
11361
11362 /**
11363  * @class Roo.data.Connection
11364  * @extends Roo.util.Observable
11365  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11366  * either to a configured URL, or to a URL specified at request time.<br><br>
11367  * <p>
11368  * Requests made by this class are asynchronous, and will return immediately. No data from
11369  * the server will be available to the statement immediately following the {@link #request} call.
11370  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11371  * <p>
11372  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11373  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11374  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11375  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11376  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11377  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11378  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11379  * standard DOM methods.
11380  * @constructor
11381  * @param {Object} config a configuration object.
11382  */
11383 Roo.data.Connection = function(config){
11384     Roo.apply(this, config);
11385     this.addEvents({
11386         /**
11387          * @event beforerequest
11388          * Fires before a network request is made to retrieve a data object.
11389          * @param {Connection} conn This Connection object.
11390          * @param {Object} options The options config object passed to the {@link #request} method.
11391          */
11392         "beforerequest" : true,
11393         /**
11394          * @event requestcomplete
11395          * Fires if the request was successfully completed.
11396          * @param {Connection} conn This Connection object.
11397          * @param {Object} response The XHR object containing the response data.
11398          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11399          * @param {Object} options The options config object passed to the {@link #request} method.
11400          */
11401         "requestcomplete" : true,
11402         /**
11403          * @event requestexception
11404          * Fires if an error HTTP status was returned from the server.
11405          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11406          * @param {Connection} conn This Connection object.
11407          * @param {Object} response The XHR object containing the response data.
11408          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11409          * @param {Object} options The options config object passed to the {@link #request} method.
11410          */
11411         "requestexception" : true
11412     });
11413     Roo.data.Connection.superclass.constructor.call(this);
11414 };
11415
11416 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11417     /**
11418      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11419      */
11420     /**
11421      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11422      * extra parameters to each request made by this object. (defaults to undefined)
11423      */
11424     /**
11425      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11426      *  to each request made by this object. (defaults to undefined)
11427      */
11428     /**
11429      * @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)
11430      */
11431     /**
11432      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11433      */
11434     timeout : 30000,
11435     /**
11436      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11437      * @type Boolean
11438      */
11439     autoAbort:false,
11440
11441     /**
11442      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11443      * @type Boolean
11444      */
11445     disableCaching: true,
11446
11447     /**
11448      * Sends an HTTP request to a remote server.
11449      * @param {Object} options An object which may contain the following properties:<ul>
11450      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11451      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11452      * request, a url encoded string or a function to call to get either.</li>
11453      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11454      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11455      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11456      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11457      * <li>options {Object} The parameter to the request call.</li>
11458      * <li>success {Boolean} True if the request succeeded.</li>
11459      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11460      * </ul></li>
11461      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11462      * The callback is passed the following parameters:<ul>
11463      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11464      * <li>options {Object} The parameter to the request call.</li>
11465      * </ul></li>
11466      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11467      * The callback is passed the following parameters:<ul>
11468      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11469      * <li>options {Object} The parameter to the request call.</li>
11470      * </ul></li>
11471      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11472      * for the callback function. Defaults to the browser window.</li>
11473      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11474      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11475      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11476      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11477      * params for the post data. Any params will be appended to the URL.</li>
11478      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11479      * </ul>
11480      * @return {Number} transactionId
11481      */
11482     request : function(o){
11483         if(this.fireEvent("beforerequest", this, o) !== false){
11484             var p = o.params;
11485
11486             if(typeof p == "function"){
11487                 p = p.call(o.scope||window, o);
11488             }
11489             if(typeof p == "object"){
11490                 p = Roo.urlEncode(o.params);
11491             }
11492             if(this.extraParams){
11493                 var extras = Roo.urlEncode(this.extraParams);
11494                 p = p ? (p + '&' + extras) : extras;
11495             }
11496
11497             var url = o.url || this.url;
11498             if(typeof url == 'function'){
11499                 url = url.call(o.scope||window, o);
11500             }
11501
11502             if(o.form){
11503                 var form = Roo.getDom(o.form);
11504                 url = url || form.action;
11505
11506                 var enctype = form.getAttribute("enctype");
11507                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11508                     return this.doFormUpload(o, p, url);
11509                 }
11510                 var f = Roo.lib.Ajax.serializeForm(form);
11511                 p = p ? (p + '&' + f) : f;
11512             }
11513
11514             var hs = o.headers;
11515             if(this.defaultHeaders){
11516                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11517                 if(!o.headers){
11518                     o.headers = hs;
11519                 }
11520             }
11521
11522             var cb = {
11523                 success: this.handleResponse,
11524                 failure: this.handleFailure,
11525                 scope: this,
11526                 argument: {options: o},
11527                 timeout : o.timeout || this.timeout
11528             };
11529
11530             var method = o.method||this.method||(p ? "POST" : "GET");
11531
11532             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11533                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11534             }
11535
11536             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11537                 if(o.autoAbort){
11538                     this.abort();
11539                 }
11540             }else if(this.autoAbort !== false){
11541                 this.abort();
11542             }
11543
11544             if((method == 'GET' && p) || o.xmlData){
11545                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11546                 p = '';
11547             }
11548             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11549             return this.transId;
11550         }else{
11551             Roo.callback(o.callback, o.scope, [o, null, null]);
11552             return null;
11553         }
11554     },
11555
11556     /**
11557      * Determine whether this object has a request outstanding.
11558      * @param {Number} transactionId (Optional) defaults to the last transaction
11559      * @return {Boolean} True if there is an outstanding request.
11560      */
11561     isLoading : function(transId){
11562         if(transId){
11563             return Roo.lib.Ajax.isCallInProgress(transId);
11564         }else{
11565             return this.transId ? true : false;
11566         }
11567     },
11568
11569     /**
11570      * Aborts any outstanding request.
11571      * @param {Number} transactionId (Optional) defaults to the last transaction
11572      */
11573     abort : function(transId){
11574         if(transId || this.isLoading()){
11575             Roo.lib.Ajax.abort(transId || this.transId);
11576         }
11577     },
11578
11579     // private
11580     handleResponse : function(response){
11581         this.transId = false;
11582         var options = response.argument.options;
11583         response.argument = options ? options.argument : null;
11584         this.fireEvent("requestcomplete", this, response, options);
11585         Roo.callback(options.success, options.scope, [response, options]);
11586         Roo.callback(options.callback, options.scope, [options, true, response]);
11587     },
11588
11589     // private
11590     handleFailure : function(response, e){
11591         this.transId = false;
11592         var options = response.argument.options;
11593         response.argument = options ? options.argument : null;
11594         this.fireEvent("requestexception", this, response, options, e);
11595         Roo.callback(options.failure, options.scope, [response, options]);
11596         Roo.callback(options.callback, options.scope, [options, false, response]);
11597     },
11598
11599     // private
11600     doFormUpload : function(o, ps, url){
11601         var id = Roo.id();
11602         var frame = document.createElement('iframe');
11603         frame.id = id;
11604         frame.name = id;
11605         frame.className = 'x-hidden';
11606         if(Roo.isIE){
11607             frame.src = Roo.SSL_SECURE_URL;
11608         }
11609         document.body.appendChild(frame);
11610
11611         if(Roo.isIE){
11612            document.frames[id].name = id;
11613         }
11614
11615         var form = Roo.getDom(o.form);
11616         form.target = id;
11617         form.method = 'POST';
11618         form.enctype = form.encoding = 'multipart/form-data';
11619         if(url){
11620             form.action = url;
11621         }
11622
11623         var hiddens, hd;
11624         if(ps){ // add dynamic params
11625             hiddens = [];
11626             ps = Roo.urlDecode(ps, false);
11627             for(var k in ps){
11628                 if(ps.hasOwnProperty(k)){
11629                     hd = document.createElement('input');
11630                     hd.type = 'hidden';
11631                     hd.name = k;
11632                     hd.value = ps[k];
11633                     form.appendChild(hd);
11634                     hiddens.push(hd);
11635                 }
11636             }
11637         }
11638
11639         function cb(){
11640             var r = {  // bogus response object
11641                 responseText : '',
11642                 responseXML : null
11643             };
11644
11645             r.argument = o ? o.argument : null;
11646
11647             try { //
11648                 var doc;
11649                 if(Roo.isIE){
11650                     doc = frame.contentWindow.document;
11651                 }else {
11652                     doc = (frame.contentDocument || window.frames[id].document);
11653                 }
11654                 if(doc && doc.body){
11655                     r.responseText = doc.body.innerHTML;
11656                 }
11657                 if(doc && doc.XMLDocument){
11658                     r.responseXML = doc.XMLDocument;
11659                 }else {
11660                     r.responseXML = doc;
11661                 }
11662             }
11663             catch(e) {
11664                 // ignore
11665             }
11666
11667             Roo.EventManager.removeListener(frame, 'load', cb, this);
11668
11669             this.fireEvent("requestcomplete", this, r, o);
11670             Roo.callback(o.success, o.scope, [r, o]);
11671             Roo.callback(o.callback, o.scope, [o, true, r]);
11672
11673             setTimeout(function(){document.body.removeChild(frame);}, 100);
11674         }
11675
11676         Roo.EventManager.on(frame, 'load', cb, this);
11677         form.submit();
11678
11679         if(hiddens){ // remove dynamic params
11680             for(var i = 0, len = hiddens.length; i < len; i++){
11681                 form.removeChild(hiddens[i]);
11682             }
11683         }
11684     }
11685 });
11686 /*
11687  * Based on:
11688  * Ext JS Library 1.1.1
11689  * Copyright(c) 2006-2007, Ext JS, LLC.
11690  *
11691  * Originally Released Under LGPL - original licence link has changed is not relivant.
11692  *
11693  * Fork - LGPL
11694  * <script type="text/javascript">
11695  */
11696  
11697 /**
11698  * Global Ajax request class.
11699  * 
11700  * @class Roo.Ajax
11701  * @extends Roo.data.Connection
11702  * @static
11703  * 
11704  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11705  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11706  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11707  * @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)
11708  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11709  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11710  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11711  */
11712 Roo.Ajax = new Roo.data.Connection({
11713     // fix up the docs
11714     /**
11715      * @scope Roo.Ajax
11716      * @type {Boolear} 
11717      */
11718     autoAbort : false,
11719
11720     /**
11721      * Serialize the passed form into a url encoded string
11722      * @scope Roo.Ajax
11723      * @param {String/HTMLElement} form
11724      * @return {String}
11725      */
11726     serializeForm : function(form){
11727         return Roo.lib.Ajax.serializeForm(form);
11728     }
11729 });/*
11730  * Based on:
11731  * Ext JS Library 1.1.1
11732  * Copyright(c) 2006-2007, Ext JS, LLC.
11733  *
11734  * Originally Released Under LGPL - original licence link has changed is not relivant.
11735  *
11736  * Fork - LGPL
11737  * <script type="text/javascript">
11738  */
11739
11740  
11741 /**
11742  * @class Roo.UpdateManager
11743  * @extends Roo.util.Observable
11744  * Provides AJAX-style update for Element object.<br><br>
11745  * Usage:<br>
11746  * <pre><code>
11747  * // Get it from a Roo.Element object
11748  * var el = Roo.get("foo");
11749  * var mgr = el.getUpdateManager();
11750  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11751  * ...
11752  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11753  * <br>
11754  * // or directly (returns the same UpdateManager instance)
11755  * var mgr = new Roo.UpdateManager("myElementId");
11756  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11757  * mgr.on("update", myFcnNeedsToKnow);
11758  * <br>
11759    // short handed call directly from the element object
11760    Roo.get("foo").load({
11761         url: "bar.php",
11762         scripts:true,
11763         params: "for=bar",
11764         text: "Loading Foo..."
11765    });
11766  * </code></pre>
11767  * @constructor
11768  * Create new UpdateManager directly.
11769  * @param {String/HTMLElement/Roo.Element} el The element to update
11770  * @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).
11771  */
11772 Roo.UpdateManager = function(el, forceNew){
11773     el = Roo.get(el);
11774     if(!forceNew && el.updateManager){
11775         return el.updateManager;
11776     }
11777     /**
11778      * The Element object
11779      * @type Roo.Element
11780      */
11781     this.el = el;
11782     /**
11783      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11784      * @type String
11785      */
11786     this.defaultUrl = null;
11787
11788     this.addEvents({
11789         /**
11790          * @event beforeupdate
11791          * Fired before an update is made, return false from your handler and the update is cancelled.
11792          * @param {Roo.Element} el
11793          * @param {String/Object/Function} url
11794          * @param {String/Object} params
11795          */
11796         "beforeupdate": true,
11797         /**
11798          * @event update
11799          * Fired after successful update is made.
11800          * @param {Roo.Element} el
11801          * @param {Object} oResponseObject The response Object
11802          */
11803         "update": true,
11804         /**
11805          * @event failure
11806          * Fired on update failure.
11807          * @param {Roo.Element} el
11808          * @param {Object} oResponseObject The response Object
11809          */
11810         "failure": true
11811     });
11812     var d = Roo.UpdateManager.defaults;
11813     /**
11814      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11815      * @type String
11816      */
11817     this.sslBlankUrl = d.sslBlankUrl;
11818     /**
11819      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11820      * @type Boolean
11821      */
11822     this.disableCaching = d.disableCaching;
11823     /**
11824      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11825      * @type String
11826      */
11827     this.indicatorText = d.indicatorText;
11828     /**
11829      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11830      * @type String
11831      */
11832     this.showLoadIndicator = d.showLoadIndicator;
11833     /**
11834      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11835      * @type Number
11836      */
11837     this.timeout = d.timeout;
11838
11839     /**
11840      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11841      * @type Boolean
11842      */
11843     this.loadScripts = d.loadScripts;
11844
11845     /**
11846      * Transaction object of current executing transaction
11847      */
11848     this.transaction = null;
11849
11850     /**
11851      * @private
11852      */
11853     this.autoRefreshProcId = null;
11854     /**
11855      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11856      * @type Function
11857      */
11858     this.refreshDelegate = this.refresh.createDelegate(this);
11859     /**
11860      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11861      * @type Function
11862      */
11863     this.updateDelegate = this.update.createDelegate(this);
11864     /**
11865      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11866      * @type Function
11867      */
11868     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11869     /**
11870      * @private
11871      */
11872     this.successDelegate = this.processSuccess.createDelegate(this);
11873     /**
11874      * @private
11875      */
11876     this.failureDelegate = this.processFailure.createDelegate(this);
11877
11878     if(!this.renderer){
11879      /**
11880       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11881       */
11882     this.renderer = new Roo.UpdateManager.BasicRenderer();
11883     }
11884     
11885     Roo.UpdateManager.superclass.constructor.call(this);
11886 };
11887
11888 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11889     /**
11890      * Get the Element this UpdateManager is bound to
11891      * @return {Roo.Element} The element
11892      */
11893     getEl : function(){
11894         return this.el;
11895     },
11896     /**
11897      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11898      * @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:
11899 <pre><code>
11900 um.update({<br/>
11901     url: "your-url.php",<br/>
11902     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11903     callback: yourFunction,<br/>
11904     scope: yourObject, //(optional scope)  <br/>
11905     discardUrl: false, <br/>
11906     nocache: false,<br/>
11907     text: "Loading...",<br/>
11908     timeout: 30,<br/>
11909     scripts: false<br/>
11910 });
11911 </code></pre>
11912      * The only required property is url. The optional properties nocache, text and scripts
11913      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11914      * @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}
11915      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11916      * @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.
11917      */
11918     update : function(url, params, callback, discardUrl){
11919         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11920             var method = this.method,
11921                 cfg;
11922             if(typeof url == "object"){ // must be config object
11923                 cfg = url;
11924                 url = cfg.url;
11925                 params = params || cfg.params;
11926                 callback = callback || cfg.callback;
11927                 discardUrl = discardUrl || cfg.discardUrl;
11928                 if(callback && cfg.scope){
11929                     callback = callback.createDelegate(cfg.scope);
11930                 }
11931                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11932                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11933                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11934                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11935                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11936             }
11937             this.showLoading();
11938             if(!discardUrl){
11939                 this.defaultUrl = url;
11940             }
11941             if(typeof url == "function"){
11942                 url = url.call(this);
11943             }
11944
11945             method = method || (params ? "POST" : "GET");
11946             if(method == "GET"){
11947                 url = this.prepareUrl(url);
11948             }
11949
11950             var o = Roo.apply(cfg ||{}, {
11951                 url : url,
11952                 params: params,
11953                 success: this.successDelegate,
11954                 failure: this.failureDelegate,
11955                 callback: undefined,
11956                 timeout: (this.timeout*1000),
11957                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11958             });
11959             Roo.log("updated manager called with timeout of " + o.timeout);
11960             this.transaction = Roo.Ajax.request(o);
11961         }
11962     },
11963
11964     /**
11965      * 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.
11966      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11967      * @param {String/HTMLElement} form The form Id or form element
11968      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11969      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11970      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11971      */
11972     formUpdate : function(form, url, reset, callback){
11973         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11974             if(typeof url == "function"){
11975                 url = url.call(this);
11976             }
11977             form = Roo.getDom(form);
11978             this.transaction = Roo.Ajax.request({
11979                 form: form,
11980                 url:url,
11981                 success: this.successDelegate,
11982                 failure: this.failureDelegate,
11983                 timeout: (this.timeout*1000),
11984                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11985             });
11986             this.showLoading.defer(1, this);
11987         }
11988     },
11989
11990     /**
11991      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11992      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11993      */
11994     refresh : function(callback){
11995         if(this.defaultUrl == null){
11996             return;
11997         }
11998         this.update(this.defaultUrl, null, callback, true);
11999     },
12000
12001     /**
12002      * Set this element to auto refresh.
12003      * @param {Number} interval How often to update (in seconds).
12004      * @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)
12005      * @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}
12006      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12007      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12008      */
12009     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12010         if(refreshNow){
12011             this.update(url || this.defaultUrl, params, callback, true);
12012         }
12013         if(this.autoRefreshProcId){
12014             clearInterval(this.autoRefreshProcId);
12015         }
12016         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12017     },
12018
12019     /**
12020      * Stop auto refresh on this element.
12021      */
12022      stopAutoRefresh : function(){
12023         if(this.autoRefreshProcId){
12024             clearInterval(this.autoRefreshProcId);
12025             delete this.autoRefreshProcId;
12026         }
12027     },
12028
12029     isAutoRefreshing : function(){
12030        return this.autoRefreshProcId ? true : false;
12031     },
12032     /**
12033      * Called to update the element to "Loading" state. Override to perform custom action.
12034      */
12035     showLoading : function(){
12036         if(this.showLoadIndicator){
12037             this.el.update(this.indicatorText);
12038         }
12039     },
12040
12041     /**
12042      * Adds unique parameter to query string if disableCaching = true
12043      * @private
12044      */
12045     prepareUrl : function(url){
12046         if(this.disableCaching){
12047             var append = "_dc=" + (new Date().getTime());
12048             if(url.indexOf("?") !== -1){
12049                 url += "&" + append;
12050             }else{
12051                 url += "?" + append;
12052             }
12053         }
12054         return url;
12055     },
12056
12057     /**
12058      * @private
12059      */
12060     processSuccess : function(response){
12061         this.transaction = null;
12062         if(response.argument.form && response.argument.reset){
12063             try{ // put in try/catch since some older FF releases had problems with this
12064                 response.argument.form.reset();
12065             }catch(e){}
12066         }
12067         if(this.loadScripts){
12068             this.renderer.render(this.el, response, this,
12069                 this.updateComplete.createDelegate(this, [response]));
12070         }else{
12071             this.renderer.render(this.el, response, this);
12072             this.updateComplete(response);
12073         }
12074     },
12075
12076     updateComplete : function(response){
12077         this.fireEvent("update", this.el, response);
12078         if(typeof response.argument.callback == "function"){
12079             response.argument.callback(this.el, true, response);
12080         }
12081     },
12082
12083     /**
12084      * @private
12085      */
12086     processFailure : function(response){
12087         this.transaction = null;
12088         this.fireEvent("failure", this.el, response);
12089         if(typeof response.argument.callback == "function"){
12090             response.argument.callback(this.el, false, response);
12091         }
12092     },
12093
12094     /**
12095      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12096      * @param {Object} renderer The object implementing the render() method
12097      */
12098     setRenderer : function(renderer){
12099         this.renderer = renderer;
12100     },
12101
12102     getRenderer : function(){
12103        return this.renderer;
12104     },
12105
12106     /**
12107      * Set the defaultUrl used for updates
12108      * @param {String/Function} defaultUrl The url or a function to call to get the url
12109      */
12110     setDefaultUrl : function(defaultUrl){
12111         this.defaultUrl = defaultUrl;
12112     },
12113
12114     /**
12115      * Aborts the executing transaction
12116      */
12117     abort : function(){
12118         if(this.transaction){
12119             Roo.Ajax.abort(this.transaction);
12120         }
12121     },
12122
12123     /**
12124      * Returns true if an update is in progress
12125      * @return {Boolean}
12126      */
12127     isUpdating : function(){
12128         if(this.transaction){
12129             return Roo.Ajax.isLoading(this.transaction);
12130         }
12131         return false;
12132     }
12133 });
12134
12135 /**
12136  * @class Roo.UpdateManager.defaults
12137  * @static (not really - but it helps the doc tool)
12138  * The defaults collection enables customizing the default properties of UpdateManager
12139  */
12140    Roo.UpdateManager.defaults = {
12141        /**
12142          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12143          * @type Number
12144          */
12145          timeout : 30,
12146
12147          /**
12148          * True to process scripts by default (Defaults to false).
12149          * @type Boolean
12150          */
12151         loadScripts : false,
12152
12153         /**
12154         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12155         * @type String
12156         */
12157         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12158         /**
12159          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12160          * @type Boolean
12161          */
12162         disableCaching : false,
12163         /**
12164          * Whether to show indicatorText when loading (Defaults to true).
12165          * @type Boolean
12166          */
12167         showLoadIndicator : true,
12168         /**
12169          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12170          * @type String
12171          */
12172         indicatorText : '<div class="loading-indicator">Loading...</div>'
12173    };
12174
12175 /**
12176  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12177  *Usage:
12178  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12179  * @param {String/HTMLElement/Roo.Element} el The element to update
12180  * @param {String} url The url
12181  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12182  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12183  * @static
12184  * @deprecated
12185  * @member Roo.UpdateManager
12186  */
12187 Roo.UpdateManager.updateElement = function(el, url, params, options){
12188     var um = Roo.get(el, true).getUpdateManager();
12189     Roo.apply(um, options);
12190     um.update(url, params, options ? options.callback : null);
12191 };
12192 // alias for backwards compat
12193 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12194 /**
12195  * @class Roo.UpdateManager.BasicRenderer
12196  * Default Content renderer. Updates the elements innerHTML with the responseText.
12197  */
12198 Roo.UpdateManager.BasicRenderer = function(){};
12199
12200 Roo.UpdateManager.BasicRenderer.prototype = {
12201     /**
12202      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12203      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12204      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12205      * @param {Roo.Element} el The element being rendered
12206      * @param {Object} response The YUI Connect response object
12207      * @param {UpdateManager} updateManager The calling update manager
12208      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12209      */
12210      render : function(el, response, updateManager, callback){
12211         el.update(response.responseText, updateManager.loadScripts, callback);
12212     }
12213 };
12214 /*
12215  * Based on:
12216  * Roo JS
12217  * (c)) Alan Knowles
12218  * Licence : LGPL
12219  */
12220
12221
12222 /**
12223  * @class Roo.DomTemplate
12224  * @extends Roo.Template
12225  * An effort at a dom based template engine..
12226  *
12227  * Similar to XTemplate, except it uses dom parsing to create the template..
12228  *
12229  * Supported features:
12230  *
12231  *  Tags:
12232
12233 <pre><code>
12234       {a_variable} - output encoded.
12235       {a_variable.format:("Y-m-d")} - call a method on the variable
12236       {a_variable:raw} - unencoded output
12237       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12238       {a_variable:this.method_on_template(...)} - call a method on the template object.
12239  
12240 </code></pre>
12241  *  The tpl tag:
12242 <pre><code>
12243         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12244         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12245         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12246         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12247   
12248 </code></pre>
12249  *      
12250  */
12251 Roo.DomTemplate = function()
12252 {
12253      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12254      if (this.html) {
12255         this.compile();
12256      }
12257 };
12258
12259
12260 Roo.extend(Roo.DomTemplate, Roo.Template, {
12261     /**
12262      * id counter for sub templates.
12263      */
12264     id : 0,
12265     /**
12266      * flag to indicate if dom parser is inside a pre,
12267      * it will strip whitespace if not.
12268      */
12269     inPre : false,
12270     
12271     /**
12272      * The various sub templates
12273      */
12274     tpls : false,
12275     
12276     
12277     
12278     /**
12279      *
12280      * basic tag replacing syntax
12281      * WORD:WORD()
12282      *
12283      * // you can fake an object call by doing this
12284      *  x.t:(test,tesT) 
12285      * 
12286      */
12287     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12288     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12289     
12290     iterChild : function (node, method) {
12291         
12292         var oldPre = this.inPre;
12293         if (node.tagName == 'PRE') {
12294             this.inPre = true;
12295         }
12296         for( var i = 0; i < node.childNodes.length; i++) {
12297             method.call(this, node.childNodes[i]);
12298         }
12299         this.inPre = oldPre;
12300     },
12301     
12302     
12303     
12304     /**
12305      * compile the template
12306      *
12307      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12308      *
12309      */
12310     compile: function()
12311     {
12312         var s = this.html;
12313         
12314         // covert the html into DOM...
12315         var doc = false;
12316         var div =false;
12317         try {
12318             doc = document.implementation.createHTMLDocument("");
12319             doc.documentElement.innerHTML =   this.html  ;
12320             div = doc.documentElement;
12321         } catch (e) {
12322             // old IE... - nasty -- it causes all sorts of issues.. with
12323             // images getting pulled from server..
12324             div = document.createElement('div');
12325             div.innerHTML = this.html;
12326         }
12327         //doc.documentElement.innerHTML = htmlBody
12328          
12329         
12330         
12331         this.tpls = [];
12332         var _t = this;
12333         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12334         
12335         var tpls = this.tpls;
12336         
12337         // create a top level template from the snippet..
12338         
12339         //Roo.log(div.innerHTML);
12340         
12341         var tpl = {
12342             uid : 'master',
12343             id : this.id++,
12344             attr : false,
12345             value : false,
12346             body : div.innerHTML,
12347             
12348             forCall : false,
12349             execCall : false,
12350             dom : div,
12351             isTop : true
12352             
12353         };
12354         tpls.unshift(tpl);
12355         
12356         
12357         // compile them...
12358         this.tpls = [];
12359         Roo.each(tpls, function(tp){
12360             this.compileTpl(tp);
12361             this.tpls[tp.id] = tp;
12362         }, this);
12363         
12364         this.master = tpls[0];
12365         return this;
12366         
12367         
12368     },
12369     
12370     compileNode : function(node, istop) {
12371         // test for
12372         //Roo.log(node);
12373         
12374         
12375         // skip anything not a tag..
12376         if (node.nodeType != 1) {
12377             if (node.nodeType == 3 && !this.inPre) {
12378                 // reduce white space..
12379                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12380                 
12381             }
12382             return;
12383         }
12384         
12385         var tpl = {
12386             uid : false,
12387             id : false,
12388             attr : false,
12389             value : false,
12390             body : '',
12391             
12392             forCall : false,
12393             execCall : false,
12394             dom : false,
12395             isTop : istop
12396             
12397             
12398         };
12399         
12400         
12401         switch(true) {
12402             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12403             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12404             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12405             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12406             // no default..
12407         }
12408         
12409         
12410         if (!tpl.attr) {
12411             // just itterate children..
12412             this.iterChild(node,this.compileNode);
12413             return;
12414         }
12415         tpl.uid = this.id++;
12416         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12417         node.removeAttribute('roo-'+ tpl.attr);
12418         if (tpl.attr != 'name') {
12419             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12420             node.parentNode.replaceChild(placeholder,  node);
12421         } else {
12422             
12423             var placeholder =  document.createElement('span');
12424             placeholder.className = 'roo-tpl-' + tpl.value;
12425             node.parentNode.replaceChild(placeholder,  node);
12426         }
12427         
12428         // parent now sees '{domtplXXXX}
12429         this.iterChild(node,this.compileNode);
12430         
12431         // we should now have node body...
12432         var div = document.createElement('div');
12433         div.appendChild(node);
12434         tpl.dom = node;
12435         // this has the unfortunate side effect of converting tagged attributes
12436         // eg. href="{...}" into %7C...%7D
12437         // this has been fixed by searching for those combo's although it's a bit hacky..
12438         
12439         
12440         tpl.body = div.innerHTML;
12441         
12442         
12443          
12444         tpl.id = tpl.uid;
12445         switch(tpl.attr) {
12446             case 'for' :
12447                 switch (tpl.value) {
12448                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12449                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12450                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12451                 }
12452                 break;
12453             
12454             case 'exec':
12455                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12456                 break;
12457             
12458             case 'if':     
12459                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12460                 break;
12461             
12462             case 'name':
12463                 tpl.id  = tpl.value; // replace non characters???
12464                 break;
12465             
12466         }
12467         
12468         
12469         this.tpls.push(tpl);
12470         
12471         
12472         
12473     },
12474     
12475     
12476     
12477     
12478     /**
12479      * Compile a segment of the template into a 'sub-template'
12480      *
12481      * 
12482      * 
12483      *
12484      */
12485     compileTpl : function(tpl)
12486     {
12487         var fm = Roo.util.Format;
12488         var useF = this.disableFormats !== true;
12489         
12490         var sep = Roo.isGecko ? "+\n" : ",\n";
12491         
12492         var undef = function(str) {
12493             Roo.debug && Roo.log("Property not found :"  + str);
12494             return '';
12495         };
12496           
12497         //Roo.log(tpl.body);
12498         
12499         
12500         
12501         var fn = function(m, lbrace, name, format, args)
12502         {
12503             //Roo.log("ARGS");
12504             //Roo.log(arguments);
12505             args = args ? args.replace(/\\'/g,"'") : args;
12506             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12507             if (typeof(format) == 'undefined') {
12508                 format =  'htmlEncode'; 
12509             }
12510             if (format == 'raw' ) {
12511                 format = false;
12512             }
12513             
12514             if(name.substr(0, 6) == 'domtpl'){
12515                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12516             }
12517             
12518             // build an array of options to determine if value is undefined..
12519             
12520             // basically get 'xxxx.yyyy' then do
12521             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12522             //    (function () { Roo.log("Property not found"); return ''; })() :
12523             //    ......
12524             
12525             var udef_ar = [];
12526             var lookfor = '';
12527             Roo.each(name.split('.'), function(st) {
12528                 lookfor += (lookfor.length ? '.': '') + st;
12529                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12530             });
12531             
12532             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12533             
12534             
12535             if(format && useF){
12536                 
12537                 args = args ? ',' + args : "";
12538                  
12539                 if(format.substr(0, 5) != "this."){
12540                     format = "fm." + format + '(';
12541                 }else{
12542                     format = 'this.call("'+ format.substr(5) + '", ';
12543                     args = ", values";
12544                 }
12545                 
12546                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12547             }
12548              
12549             if (args && args.length) {
12550                 // called with xxyx.yuu:(test,test)
12551                 // change to ()
12552                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12553             }
12554             // raw.. - :raw modifier..
12555             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12556             
12557         };
12558         var body;
12559         // branched to use + in gecko and [].join() in others
12560         if(Roo.isGecko){
12561             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12562                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12563                     "';};};";
12564         }else{
12565             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12566             body.push(tpl.body.replace(/(\r\n|\n)/g,
12567                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12568             body.push("'].join('');};};");
12569             body = body.join('');
12570         }
12571         
12572         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12573        
12574         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12575         eval(body);
12576         
12577         return this;
12578     },
12579      
12580     /**
12581      * same as applyTemplate, except it's done to one of the subTemplates
12582      * when using named templates, you can do:
12583      *
12584      * var str = pl.applySubTemplate('your-name', values);
12585      *
12586      * 
12587      * @param {Number} id of the template
12588      * @param {Object} values to apply to template
12589      * @param {Object} parent (normaly the instance of this object)
12590      */
12591     applySubTemplate : function(id, values, parent)
12592     {
12593         
12594         
12595         var t = this.tpls[id];
12596         
12597         
12598         try { 
12599             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12600                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12601                 return '';
12602             }
12603         } catch(e) {
12604             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12605             Roo.log(values);
12606           
12607             return '';
12608         }
12609         try { 
12610             
12611             if(t.execCall && t.execCall.call(this, values, parent)){
12612                 return '';
12613             }
12614         } catch(e) {
12615             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12616             Roo.log(values);
12617             return '';
12618         }
12619         
12620         try {
12621             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12622             parent = t.target ? values : parent;
12623             if(t.forCall && vs instanceof Array){
12624                 var buf = [];
12625                 for(var i = 0, len = vs.length; i < len; i++){
12626                     try {
12627                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12628                     } catch (e) {
12629                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12630                         Roo.log(e.body);
12631                         //Roo.log(t.compiled);
12632                         Roo.log(vs[i]);
12633                     }   
12634                 }
12635                 return buf.join('');
12636             }
12637         } catch (e) {
12638             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12639             Roo.log(values);
12640             return '';
12641         }
12642         try {
12643             return t.compiled.call(this, vs, parent);
12644         } catch (e) {
12645             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12646             Roo.log(e.body);
12647             //Roo.log(t.compiled);
12648             Roo.log(values);
12649             return '';
12650         }
12651     },
12652
12653    
12654
12655     applyTemplate : function(values){
12656         return this.master.compiled.call(this, values, {});
12657         //var s = this.subs;
12658     },
12659
12660     apply : function(){
12661         return this.applyTemplate.apply(this, arguments);
12662     }
12663
12664  });
12665
12666 Roo.DomTemplate.from = function(el){
12667     el = Roo.getDom(el);
12668     return new Roo.Domtemplate(el.value || el.innerHTML);
12669 };/*
12670  * Based on:
12671  * Ext JS Library 1.1.1
12672  * Copyright(c) 2006-2007, Ext JS, LLC.
12673  *
12674  * Originally Released Under LGPL - original licence link has changed is not relivant.
12675  *
12676  * Fork - LGPL
12677  * <script type="text/javascript">
12678  */
12679
12680 /**
12681  * @class Roo.util.DelayedTask
12682  * Provides a convenient method of performing setTimeout where a new
12683  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12684  * You can use this class to buffer
12685  * the keypress events for a certain number of milliseconds, and perform only if they stop
12686  * for that amount of time.
12687  * @constructor The parameters to this constructor serve as defaults and are not required.
12688  * @param {Function} fn (optional) The default function to timeout
12689  * @param {Object} scope (optional) The default scope of that timeout
12690  * @param {Array} args (optional) The default Array of arguments
12691  */
12692 Roo.util.DelayedTask = function(fn, scope, args){
12693     var id = null, d, t;
12694
12695     var call = function(){
12696         var now = new Date().getTime();
12697         if(now - t >= d){
12698             clearInterval(id);
12699             id = null;
12700             fn.apply(scope, args || []);
12701         }
12702     };
12703     /**
12704      * Cancels any pending timeout and queues a new one
12705      * @param {Number} delay The milliseconds to delay
12706      * @param {Function} newFn (optional) Overrides function passed to constructor
12707      * @param {Object} newScope (optional) Overrides scope passed to constructor
12708      * @param {Array} newArgs (optional) Overrides args passed to constructor
12709      */
12710     this.delay = function(delay, newFn, newScope, newArgs){
12711         if(id && delay != d){
12712             this.cancel();
12713         }
12714         d = delay;
12715         t = new Date().getTime();
12716         fn = newFn || fn;
12717         scope = newScope || scope;
12718         args = newArgs || args;
12719         if(!id){
12720             id = setInterval(call, d);
12721         }
12722     };
12723
12724     /**
12725      * Cancel the last queued timeout
12726      */
12727     this.cancel = function(){
12728         if(id){
12729             clearInterval(id);
12730             id = null;
12731         }
12732     };
12733 };/*
12734  * Based on:
12735  * Ext JS Library 1.1.1
12736  * Copyright(c) 2006-2007, Ext JS, LLC.
12737  *
12738  * Originally Released Under LGPL - original licence link has changed is not relivant.
12739  *
12740  * Fork - LGPL
12741  * <script type="text/javascript">
12742  */
12743  
12744  
12745 Roo.util.TaskRunner = function(interval){
12746     interval = interval || 10;
12747     var tasks = [], removeQueue = [];
12748     var id = 0;
12749     var running = false;
12750
12751     var stopThread = function(){
12752         running = false;
12753         clearInterval(id);
12754         id = 0;
12755     };
12756
12757     var startThread = function(){
12758         if(!running){
12759             running = true;
12760             id = setInterval(runTasks, interval);
12761         }
12762     };
12763
12764     var removeTask = function(task){
12765         removeQueue.push(task);
12766         if(task.onStop){
12767             task.onStop();
12768         }
12769     };
12770
12771     var runTasks = function(){
12772         if(removeQueue.length > 0){
12773             for(var i = 0, len = removeQueue.length; i < len; i++){
12774                 tasks.remove(removeQueue[i]);
12775             }
12776             removeQueue = [];
12777             if(tasks.length < 1){
12778                 stopThread();
12779                 return;
12780             }
12781         }
12782         var now = new Date().getTime();
12783         for(var i = 0, len = tasks.length; i < len; ++i){
12784             var t = tasks[i];
12785             var itime = now - t.taskRunTime;
12786             if(t.interval <= itime){
12787                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12788                 t.taskRunTime = now;
12789                 if(rt === false || t.taskRunCount === t.repeat){
12790                     removeTask(t);
12791                     return;
12792                 }
12793             }
12794             if(t.duration && t.duration <= (now - t.taskStartTime)){
12795                 removeTask(t);
12796             }
12797         }
12798     };
12799
12800     /**
12801      * Queues a new task.
12802      * @param {Object} task
12803      */
12804     this.start = function(task){
12805         tasks.push(task);
12806         task.taskStartTime = new Date().getTime();
12807         task.taskRunTime = 0;
12808         task.taskRunCount = 0;
12809         startThread();
12810         return task;
12811     };
12812
12813     this.stop = function(task){
12814         removeTask(task);
12815         return task;
12816     };
12817
12818     this.stopAll = function(){
12819         stopThread();
12820         for(var i = 0, len = tasks.length; i < len; i++){
12821             if(tasks[i].onStop){
12822                 tasks[i].onStop();
12823             }
12824         }
12825         tasks = [];
12826         removeQueue = [];
12827     };
12828 };
12829
12830 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12831  * Based on:
12832  * Ext JS Library 1.1.1
12833  * Copyright(c) 2006-2007, Ext JS, LLC.
12834  *
12835  * Originally Released Under LGPL - original licence link has changed is not relivant.
12836  *
12837  * Fork - LGPL
12838  * <script type="text/javascript">
12839  */
12840
12841  
12842 /**
12843  * @class Roo.util.MixedCollection
12844  * @extends Roo.util.Observable
12845  * A Collection class that maintains both numeric indexes and keys and exposes events.
12846  * @constructor
12847  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12848  * collection (defaults to false)
12849  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12850  * and return the key value for that item.  This is used when available to look up the key on items that
12851  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12852  * equivalent to providing an implementation for the {@link #getKey} method.
12853  */
12854 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12855     this.items = [];
12856     this.map = {};
12857     this.keys = [];
12858     this.length = 0;
12859     this.addEvents({
12860         /**
12861          * @event clear
12862          * Fires when the collection is cleared.
12863          */
12864         "clear" : true,
12865         /**
12866          * @event add
12867          * Fires when an item is added to the collection.
12868          * @param {Number} index The index at which the item was added.
12869          * @param {Object} o The item added.
12870          * @param {String} key The key associated with the added item.
12871          */
12872         "add" : true,
12873         /**
12874          * @event replace
12875          * Fires when an item is replaced in the collection.
12876          * @param {String} key he key associated with the new added.
12877          * @param {Object} old The item being replaced.
12878          * @param {Object} new The new item.
12879          */
12880         "replace" : true,
12881         /**
12882          * @event remove
12883          * Fires when an item is removed from the collection.
12884          * @param {Object} o The item being removed.
12885          * @param {String} key (optional) The key associated with the removed item.
12886          */
12887         "remove" : true,
12888         "sort" : true
12889     });
12890     this.allowFunctions = allowFunctions === true;
12891     if(keyFn){
12892         this.getKey = keyFn;
12893     }
12894     Roo.util.MixedCollection.superclass.constructor.call(this);
12895 };
12896
12897 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12898     allowFunctions : false,
12899     
12900 /**
12901  * Adds an item to the collection.
12902  * @param {String} key The key to associate with the item
12903  * @param {Object} o The item to add.
12904  * @return {Object} The item added.
12905  */
12906     add : function(key, o){
12907         if(arguments.length == 1){
12908             o = arguments[0];
12909             key = this.getKey(o);
12910         }
12911         if(typeof key == "undefined" || key === null){
12912             this.length++;
12913             this.items.push(o);
12914             this.keys.push(null);
12915         }else{
12916             var old = this.map[key];
12917             if(old){
12918                 return this.replace(key, o);
12919             }
12920             this.length++;
12921             this.items.push(o);
12922             this.map[key] = o;
12923             this.keys.push(key);
12924         }
12925         this.fireEvent("add", this.length-1, o, key);
12926         return o;
12927     },
12928        
12929 /**
12930   * MixedCollection has a generic way to fetch keys if you implement getKey.
12931 <pre><code>
12932 // normal way
12933 var mc = new Roo.util.MixedCollection();
12934 mc.add(someEl.dom.id, someEl);
12935 mc.add(otherEl.dom.id, otherEl);
12936 //and so on
12937
12938 // using getKey
12939 var mc = new Roo.util.MixedCollection();
12940 mc.getKey = function(el){
12941    return el.dom.id;
12942 };
12943 mc.add(someEl);
12944 mc.add(otherEl);
12945
12946 // or via the constructor
12947 var mc = new Roo.util.MixedCollection(false, function(el){
12948    return el.dom.id;
12949 });
12950 mc.add(someEl);
12951 mc.add(otherEl);
12952 </code></pre>
12953  * @param o {Object} The item for which to find the key.
12954  * @return {Object} The key for the passed item.
12955  */
12956     getKey : function(o){
12957          return o.id; 
12958     },
12959    
12960 /**
12961  * Replaces an item in the collection.
12962  * @param {String} key The key associated with the item to replace, or the item to replace.
12963  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12964  * @return {Object}  The new item.
12965  */
12966     replace : function(key, o){
12967         if(arguments.length == 1){
12968             o = arguments[0];
12969             key = this.getKey(o);
12970         }
12971         var old = this.item(key);
12972         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12973              return this.add(key, o);
12974         }
12975         var index = this.indexOfKey(key);
12976         this.items[index] = o;
12977         this.map[key] = o;
12978         this.fireEvent("replace", key, old, o);
12979         return o;
12980     },
12981    
12982 /**
12983  * Adds all elements of an Array or an Object to the collection.
12984  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12985  * an Array of values, each of which are added to the collection.
12986  */
12987     addAll : function(objs){
12988         if(arguments.length > 1 || objs instanceof Array){
12989             var args = arguments.length > 1 ? arguments : objs;
12990             for(var i = 0, len = args.length; i < len; i++){
12991                 this.add(args[i]);
12992             }
12993         }else{
12994             for(var key in objs){
12995                 if(this.allowFunctions || typeof objs[key] != "function"){
12996                     this.add(key, objs[key]);
12997                 }
12998             }
12999         }
13000     },
13001    
13002 /**
13003  * Executes the specified function once for every item in the collection, passing each
13004  * item as the first and only parameter. returning false from the function will stop the iteration.
13005  * @param {Function} fn The function to execute for each item.
13006  * @param {Object} scope (optional) The scope in which to execute the function.
13007  */
13008     each : function(fn, scope){
13009         var items = [].concat(this.items); // each safe for removal
13010         for(var i = 0, len = items.length; i < len; i++){
13011             if(fn.call(scope || items[i], items[i], i, len) === false){
13012                 break;
13013             }
13014         }
13015     },
13016    
13017 /**
13018  * Executes the specified function once for every key in the collection, passing each
13019  * key, and its associated item as the first two parameters.
13020  * @param {Function} fn The function to execute for each item.
13021  * @param {Object} scope (optional) The scope in which to execute the function.
13022  */
13023     eachKey : function(fn, scope){
13024         for(var i = 0, len = this.keys.length; i < len; i++){
13025             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13026         }
13027     },
13028    
13029 /**
13030  * Returns the first item in the collection which elicits a true return value from the
13031  * passed selection function.
13032  * @param {Function} fn The selection function to execute for each item.
13033  * @param {Object} scope (optional) The scope in which to execute the function.
13034  * @return {Object} The first item in the collection which returned true from the selection function.
13035  */
13036     find : function(fn, scope){
13037         for(var i = 0, len = this.items.length; i < len; i++){
13038             if(fn.call(scope || window, this.items[i], this.keys[i])){
13039                 return this.items[i];
13040             }
13041         }
13042         return null;
13043     },
13044    
13045 /**
13046  * Inserts an item at the specified index in the collection.
13047  * @param {Number} index The index to insert the item at.
13048  * @param {String} key The key to associate with the new item, or the item itself.
13049  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13050  * @return {Object} The item inserted.
13051  */
13052     insert : function(index, key, o){
13053         if(arguments.length == 2){
13054             o = arguments[1];
13055             key = this.getKey(o);
13056         }
13057         if(index >= this.length){
13058             return this.add(key, o);
13059         }
13060         this.length++;
13061         this.items.splice(index, 0, o);
13062         if(typeof key != "undefined" && key != null){
13063             this.map[key] = o;
13064         }
13065         this.keys.splice(index, 0, key);
13066         this.fireEvent("add", index, o, key);
13067         return o;
13068     },
13069    
13070 /**
13071  * Removed an item from the collection.
13072  * @param {Object} o The item to remove.
13073  * @return {Object} The item removed.
13074  */
13075     remove : function(o){
13076         return this.removeAt(this.indexOf(o));
13077     },
13078    
13079 /**
13080  * Remove an item from a specified index in the collection.
13081  * @param {Number} index The index within the collection of the item to remove.
13082  */
13083     removeAt : function(index){
13084         if(index < this.length && index >= 0){
13085             this.length--;
13086             var o = this.items[index];
13087             this.items.splice(index, 1);
13088             var key = this.keys[index];
13089             if(typeof key != "undefined"){
13090                 delete this.map[key];
13091             }
13092             this.keys.splice(index, 1);
13093             this.fireEvent("remove", o, key);
13094         }
13095     },
13096    
13097 /**
13098  * Removed an item associated with the passed key fom the collection.
13099  * @param {String} key The key of the item to remove.
13100  */
13101     removeKey : function(key){
13102         return this.removeAt(this.indexOfKey(key));
13103     },
13104    
13105 /**
13106  * Returns the number of items in the collection.
13107  * @return {Number} the number of items in the collection.
13108  */
13109     getCount : function(){
13110         return this.length; 
13111     },
13112    
13113 /**
13114  * Returns index within the collection of the passed Object.
13115  * @param {Object} o The item to find the index of.
13116  * @return {Number} index of the item.
13117  */
13118     indexOf : function(o){
13119         if(!this.items.indexOf){
13120             for(var i = 0, len = this.items.length; i < len; i++){
13121                 if(this.items[i] == o) return i;
13122             }
13123             return -1;
13124         }else{
13125             return this.items.indexOf(o);
13126         }
13127     },
13128    
13129 /**
13130  * Returns index within the collection of the passed key.
13131  * @param {String} key The key to find the index of.
13132  * @return {Number} index of the key.
13133  */
13134     indexOfKey : function(key){
13135         if(!this.keys.indexOf){
13136             for(var i = 0, len = this.keys.length; i < len; i++){
13137                 if(this.keys[i] == key) return i;
13138             }
13139             return -1;
13140         }else{
13141             return this.keys.indexOf(key);
13142         }
13143     },
13144    
13145 /**
13146  * Returns the item associated with the passed key OR index. Key has priority over index.
13147  * @param {String/Number} key The key or index of the item.
13148  * @return {Object} The item associated with the passed key.
13149  */
13150     item : function(key){
13151         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13152         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13153     },
13154     
13155 /**
13156  * Returns the item at the specified index.
13157  * @param {Number} index The index of the item.
13158  * @return {Object}
13159  */
13160     itemAt : function(index){
13161         return this.items[index];
13162     },
13163     
13164 /**
13165  * Returns the item associated with the passed key.
13166  * @param {String/Number} key The key of the item.
13167  * @return {Object} The item associated with the passed key.
13168  */
13169     key : function(key){
13170         return this.map[key];
13171     },
13172    
13173 /**
13174  * Returns true if the collection contains the passed Object as an item.
13175  * @param {Object} o  The Object to look for in the collection.
13176  * @return {Boolean} True if the collection contains the Object as an item.
13177  */
13178     contains : function(o){
13179         return this.indexOf(o) != -1;
13180     },
13181    
13182 /**
13183  * Returns true if the collection contains the passed Object as a key.
13184  * @param {String} key The key to look for in the collection.
13185  * @return {Boolean} True if the collection contains the Object as a key.
13186  */
13187     containsKey : function(key){
13188         return typeof this.map[key] != "undefined";
13189     },
13190    
13191 /**
13192  * Removes all items from the collection.
13193  */
13194     clear : function(){
13195         this.length = 0;
13196         this.items = [];
13197         this.keys = [];
13198         this.map = {};
13199         this.fireEvent("clear");
13200     },
13201    
13202 /**
13203  * Returns the first item in the collection.
13204  * @return {Object} the first item in the collection..
13205  */
13206     first : function(){
13207         return this.items[0]; 
13208     },
13209    
13210 /**
13211  * Returns the last item in the collection.
13212  * @return {Object} the last item in the collection..
13213  */
13214     last : function(){
13215         return this.items[this.length-1];   
13216     },
13217     
13218     _sort : function(property, dir, fn){
13219         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13220         fn = fn || function(a, b){
13221             return a-b;
13222         };
13223         var c = [], k = this.keys, items = this.items;
13224         for(var i = 0, len = items.length; i < len; i++){
13225             c[c.length] = {key: k[i], value: items[i], index: i};
13226         }
13227         c.sort(function(a, b){
13228             var v = fn(a[property], b[property]) * dsc;
13229             if(v == 0){
13230                 v = (a.index < b.index ? -1 : 1);
13231             }
13232             return v;
13233         });
13234         for(var i = 0, len = c.length; i < len; i++){
13235             items[i] = c[i].value;
13236             k[i] = c[i].key;
13237         }
13238         this.fireEvent("sort", this);
13239     },
13240     
13241     /**
13242      * Sorts this collection with the passed comparison function
13243      * @param {String} direction (optional) "ASC" or "DESC"
13244      * @param {Function} fn (optional) comparison function
13245      */
13246     sort : function(dir, fn){
13247         this._sort("value", dir, fn);
13248     },
13249     
13250     /**
13251      * Sorts this collection by keys
13252      * @param {String} direction (optional) "ASC" or "DESC"
13253      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13254      */
13255     keySort : function(dir, fn){
13256         this._sort("key", dir, fn || function(a, b){
13257             return String(a).toUpperCase()-String(b).toUpperCase();
13258         });
13259     },
13260     
13261     /**
13262      * Returns a range of items in this collection
13263      * @param {Number} startIndex (optional) defaults to 0
13264      * @param {Number} endIndex (optional) default to the last item
13265      * @return {Array} An array of items
13266      */
13267     getRange : function(start, end){
13268         var items = this.items;
13269         if(items.length < 1){
13270             return [];
13271         }
13272         start = start || 0;
13273         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13274         var r = [];
13275         if(start <= end){
13276             for(var i = start; i <= end; i++) {
13277                     r[r.length] = items[i];
13278             }
13279         }else{
13280             for(var i = start; i >= end; i--) {
13281                     r[r.length] = items[i];
13282             }
13283         }
13284         return r;
13285     },
13286         
13287     /**
13288      * Filter the <i>objects</i> in this collection by a specific property. 
13289      * Returns a new collection that has been filtered.
13290      * @param {String} property A property on your objects
13291      * @param {String/RegExp} value Either string that the property values 
13292      * should start with or a RegExp to test against the property
13293      * @return {MixedCollection} The new filtered collection
13294      */
13295     filter : function(property, value){
13296         if(!value.exec){ // not a regex
13297             value = String(value);
13298             if(value.length == 0){
13299                 return this.clone();
13300             }
13301             value = new RegExp("^" + Roo.escapeRe(value), "i");
13302         }
13303         return this.filterBy(function(o){
13304             return o && value.test(o[property]);
13305         });
13306         },
13307     
13308     /**
13309      * Filter by a function. * Returns a new collection that has been filtered.
13310      * The passed function will be called with each 
13311      * object in the collection. If the function returns true, the value is included 
13312      * otherwise it is filtered.
13313      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13314      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13315      * @return {MixedCollection} The new filtered collection
13316      */
13317     filterBy : function(fn, scope){
13318         var r = new Roo.util.MixedCollection();
13319         r.getKey = this.getKey;
13320         var k = this.keys, it = this.items;
13321         for(var i = 0, len = it.length; i < len; i++){
13322             if(fn.call(scope||this, it[i], k[i])){
13323                                 r.add(k[i], it[i]);
13324                         }
13325         }
13326         return r;
13327     },
13328     
13329     /**
13330      * Creates a duplicate of this collection
13331      * @return {MixedCollection}
13332      */
13333     clone : function(){
13334         var r = new Roo.util.MixedCollection();
13335         var k = this.keys, it = this.items;
13336         for(var i = 0, len = it.length; i < len; i++){
13337             r.add(k[i], it[i]);
13338         }
13339         r.getKey = this.getKey;
13340         return r;
13341     }
13342 });
13343 /**
13344  * Returns the item associated with the passed key or index.
13345  * @method
13346  * @param {String/Number} key The key or index of the item.
13347  * @return {Object} The item associated with the passed key.
13348  */
13349 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13350  * Based on:
13351  * Ext JS Library 1.1.1
13352  * Copyright(c) 2006-2007, Ext JS, LLC.
13353  *
13354  * Originally Released Under LGPL - original licence link has changed is not relivant.
13355  *
13356  * Fork - LGPL
13357  * <script type="text/javascript">
13358  */
13359 /**
13360  * @class Roo.util.JSON
13361  * Modified version of Douglas Crockford"s json.js that doesn"t
13362  * mess with the Object prototype 
13363  * http://www.json.org/js.html
13364  * @singleton
13365  */
13366 Roo.util.JSON = new (function(){
13367     var useHasOwn = {}.hasOwnProperty ? true : false;
13368     
13369     // crashes Safari in some instances
13370     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13371     
13372     var pad = function(n) {
13373         return n < 10 ? "0" + n : n;
13374     };
13375     
13376     var m = {
13377         "\b": '\\b',
13378         "\t": '\\t',
13379         "\n": '\\n',
13380         "\f": '\\f',
13381         "\r": '\\r',
13382         '"' : '\\"',
13383         "\\": '\\\\'
13384     };
13385
13386     var encodeString = function(s){
13387         if (/["\\\x00-\x1f]/.test(s)) {
13388             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13389                 var c = m[b];
13390                 if(c){
13391                     return c;
13392                 }
13393                 c = b.charCodeAt();
13394                 return "\\u00" +
13395                     Math.floor(c / 16).toString(16) +
13396                     (c % 16).toString(16);
13397             }) + '"';
13398         }
13399         return '"' + s + '"';
13400     };
13401     
13402     var encodeArray = function(o){
13403         var a = ["["], b, i, l = o.length, v;
13404             for (i = 0; i < l; i += 1) {
13405                 v = o[i];
13406                 switch (typeof v) {
13407                     case "undefined":
13408                     case "function":
13409                     case "unknown":
13410                         break;
13411                     default:
13412                         if (b) {
13413                             a.push(',');
13414                         }
13415                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13416                         b = true;
13417                 }
13418             }
13419             a.push("]");
13420             return a.join("");
13421     };
13422     
13423     var encodeDate = function(o){
13424         return '"' + o.getFullYear() + "-" +
13425                 pad(o.getMonth() + 1) + "-" +
13426                 pad(o.getDate()) + "T" +
13427                 pad(o.getHours()) + ":" +
13428                 pad(o.getMinutes()) + ":" +
13429                 pad(o.getSeconds()) + '"';
13430     };
13431     
13432     /**
13433      * Encodes an Object, Array or other value
13434      * @param {Mixed} o The variable to encode
13435      * @return {String} The JSON string
13436      */
13437     this.encode = function(o)
13438     {
13439         // should this be extended to fully wrap stringify..
13440         
13441         if(typeof o == "undefined" || o === null){
13442             return "null";
13443         }else if(o instanceof Array){
13444             return encodeArray(o);
13445         }else if(o instanceof Date){
13446             return encodeDate(o);
13447         }else if(typeof o == "string"){
13448             return encodeString(o);
13449         }else if(typeof o == "number"){
13450             return isFinite(o) ? String(o) : "null";
13451         }else if(typeof o == "boolean"){
13452             return String(o);
13453         }else {
13454             var a = ["{"], b, i, v;
13455             for (i in o) {
13456                 if(!useHasOwn || o.hasOwnProperty(i)) {
13457                     v = o[i];
13458                     switch (typeof v) {
13459                     case "undefined":
13460                     case "function":
13461                     case "unknown":
13462                         break;
13463                     default:
13464                         if(b){
13465                             a.push(',');
13466                         }
13467                         a.push(this.encode(i), ":",
13468                                 v === null ? "null" : this.encode(v));
13469                         b = true;
13470                     }
13471                 }
13472             }
13473             a.push("}");
13474             return a.join("");
13475         }
13476     };
13477     
13478     /**
13479      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13480      * @param {String} json The JSON string
13481      * @return {Object} The resulting object
13482      */
13483     this.decode = function(json){
13484         
13485         return  /** eval:var:json */ eval("(" + json + ')');
13486     };
13487 })();
13488 /** 
13489  * Shorthand for {@link Roo.util.JSON#encode}
13490  * @member Roo encode 
13491  * @method */
13492 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13493 /** 
13494  * Shorthand for {@link Roo.util.JSON#decode}
13495  * @member Roo decode 
13496  * @method */
13497 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13498 /*
13499  * Based on:
13500  * Ext JS Library 1.1.1
13501  * Copyright(c) 2006-2007, Ext JS, LLC.
13502  *
13503  * Originally Released Under LGPL - original licence link has changed is not relivant.
13504  *
13505  * Fork - LGPL
13506  * <script type="text/javascript">
13507  */
13508  
13509 /**
13510  * @class Roo.util.Format
13511  * Reusable data formatting functions
13512  * @singleton
13513  */
13514 Roo.util.Format = function(){
13515     var trimRe = /^\s+|\s+$/g;
13516     return {
13517         /**
13518          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13519          * @param {String} value The string to truncate
13520          * @param {Number} length The maximum length to allow before truncating
13521          * @return {String} The converted text
13522          */
13523         ellipsis : function(value, len){
13524             if(value && value.length > len){
13525                 return value.substr(0, len-3)+"...";
13526             }
13527             return value;
13528         },
13529
13530         /**
13531          * Checks a reference and converts it to empty string if it is undefined
13532          * @param {Mixed} value Reference to check
13533          * @return {Mixed} Empty string if converted, otherwise the original value
13534          */
13535         undef : function(value){
13536             return typeof value != "undefined" ? value : "";
13537         },
13538
13539         /**
13540          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13541          * @param {String} value The string to encode
13542          * @return {String} The encoded text
13543          */
13544         htmlEncode : function(value){
13545             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13546         },
13547
13548         /**
13549          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13550          * @param {String} value The string to decode
13551          * @return {String} The decoded text
13552          */
13553         htmlDecode : function(value){
13554             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13555         },
13556
13557         /**
13558          * Trims any whitespace from either side of a string
13559          * @param {String} value The text to trim
13560          * @return {String} The trimmed text
13561          */
13562         trim : function(value){
13563             return String(value).replace(trimRe, "");
13564         },
13565
13566         /**
13567          * Returns a substring from within an original string
13568          * @param {String} value The original text
13569          * @param {Number} start The start index of the substring
13570          * @param {Number} length The length of the substring
13571          * @return {String} The substring
13572          */
13573         substr : function(value, start, length){
13574             return String(value).substr(start, length);
13575         },
13576
13577         /**
13578          * Converts a string to all lower case letters
13579          * @param {String} value The text to convert
13580          * @return {String} The converted text
13581          */
13582         lowercase : function(value){
13583             return String(value).toLowerCase();
13584         },
13585
13586         /**
13587          * Converts a string to all upper case letters
13588          * @param {String} value The text to convert
13589          * @return {String} The converted text
13590          */
13591         uppercase : function(value){
13592             return String(value).toUpperCase();
13593         },
13594
13595         /**
13596          * Converts the first character only of a string to upper case
13597          * @param {String} value The text to convert
13598          * @return {String} The converted text
13599          */
13600         capitalize : function(value){
13601             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13602         },
13603
13604         // private
13605         call : function(value, fn){
13606             if(arguments.length > 2){
13607                 var args = Array.prototype.slice.call(arguments, 2);
13608                 args.unshift(value);
13609                  
13610                 return /** eval:var:value */  eval(fn).apply(window, args);
13611             }else{
13612                 /** eval:var:value */
13613                 return /** eval:var:value */ eval(fn).call(window, value);
13614             }
13615         },
13616
13617        
13618         /**
13619          * safer version of Math.toFixed..??/
13620          * @param {Number/String} value The numeric value to format
13621          * @param {Number/String} value Decimal places 
13622          * @return {String} The formatted currency string
13623          */
13624         toFixed : function(v, n)
13625         {
13626             // why not use to fixed - precision is buggered???
13627             if (!n) {
13628                 return Math.round(v-0);
13629             }
13630             var fact = Math.pow(10,n+1);
13631             v = (Math.round((v-0)*fact))/fact;
13632             var z = (''+fact).substring(2);
13633             if (v == Math.floor(v)) {
13634                 return Math.floor(v) + '.' + z;
13635             }
13636             
13637             // now just padd decimals..
13638             var ps = String(v).split('.');
13639             var fd = (ps[1] + z);
13640             var r = fd.substring(0,n); 
13641             var rm = fd.substring(n); 
13642             if (rm < 5) {
13643                 return ps[0] + '.' + r;
13644             }
13645             r*=1; // turn it into a number;
13646             r++;
13647             if (String(r).length != n) {
13648                 ps[0]*=1;
13649                 ps[0]++;
13650                 r = String(r).substring(1); // chop the end off.
13651             }
13652             
13653             return ps[0] + '.' + r;
13654              
13655         },
13656         
13657         /**
13658          * Format a number as US currency
13659          * @param {Number/String} value The numeric value to format
13660          * @return {String} The formatted currency string
13661          */
13662         usMoney : function(v){
13663             return '$' + Roo.util.Format.number(v);
13664         },
13665         
13666         /**
13667          * Format a number
13668          * eventually this should probably emulate php's number_format
13669          * @param {Number/String} value The numeric value to format
13670          * @param {Number} decimals number of decimal places
13671          * @return {String} The formatted currency string
13672          */
13673         number : function(v,decimals)
13674         {
13675             // multiply and round.
13676             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13677             var mul = Math.pow(10, decimals);
13678             var zero = String(mul).substring(1);
13679             v = (Math.round((v-0)*mul))/mul;
13680             
13681             // if it's '0' number.. then
13682             
13683             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13684             v = String(v);
13685             var ps = v.split('.');
13686             var whole = ps[0];
13687             
13688             
13689             var r = /(\d+)(\d{3})/;
13690             // add comma's
13691             while (r.test(whole)) {
13692                 whole = whole.replace(r, '$1' + ',' + '$2');
13693             }
13694             
13695             
13696             var sub = ps[1] ?
13697                     // has decimals..
13698                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13699                     // does not have decimals
13700                     (decimals ? ('.' + zero) : '');
13701             
13702             
13703             return whole + sub ;
13704         },
13705         
13706         /**
13707          * Parse a value into a formatted date using the specified format pattern.
13708          * @param {Mixed} value The value to format
13709          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13710          * @return {String} The formatted date string
13711          */
13712         date : function(v, format){
13713             if(!v){
13714                 return "";
13715             }
13716             if(!(v instanceof Date)){
13717                 v = new Date(Date.parse(v));
13718             }
13719             return v.dateFormat(format || "m/d/Y");
13720         },
13721
13722         /**
13723          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13724          * @param {String} format Any valid date format string
13725          * @return {Function} The date formatting function
13726          */
13727         dateRenderer : function(format){
13728             return function(v){
13729                 return Roo.util.Format.date(v, format);  
13730             };
13731         },
13732
13733         // private
13734         stripTagsRE : /<\/?[^>]+>/gi,
13735         
13736         /**
13737          * Strips all HTML tags
13738          * @param {Mixed} value The text from which to strip tags
13739          * @return {String} The stripped text
13740          */
13741         stripTags : function(v){
13742             return !v ? v : String(v).replace(this.stripTagsRE, "");
13743         }
13744     };
13745 }();/*
13746  * Based on:
13747  * Ext JS Library 1.1.1
13748  * Copyright(c) 2006-2007, Ext JS, LLC.
13749  *
13750  * Originally Released Under LGPL - original licence link has changed is not relivant.
13751  *
13752  * Fork - LGPL
13753  * <script type="text/javascript">
13754  */
13755
13756
13757  
13758
13759 /**
13760  * @class Roo.MasterTemplate
13761  * @extends Roo.Template
13762  * Provides a template that can have child templates. The syntax is:
13763 <pre><code>
13764 var t = new Roo.MasterTemplate(
13765         '&lt;select name="{name}"&gt;',
13766                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13767         '&lt;/select&gt;'
13768 );
13769 t.add('options', {value: 'foo', text: 'bar'});
13770 // or you can add multiple child elements in one shot
13771 t.addAll('options', [
13772     {value: 'foo', text: 'bar'},
13773     {value: 'foo2', text: 'bar2'},
13774     {value: 'foo3', text: 'bar3'}
13775 ]);
13776 // then append, applying the master template values
13777 t.append('my-form', {name: 'my-select'});
13778 </code></pre>
13779 * A name attribute for the child template is not required if you have only one child
13780 * template or you want to refer to them by index.
13781  */
13782 Roo.MasterTemplate = function(){
13783     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13784     this.originalHtml = this.html;
13785     var st = {};
13786     var m, re = this.subTemplateRe;
13787     re.lastIndex = 0;
13788     var subIndex = 0;
13789     while(m = re.exec(this.html)){
13790         var name = m[1], content = m[2];
13791         st[subIndex] = {
13792             name: name,
13793             index: subIndex,
13794             buffer: [],
13795             tpl : new Roo.Template(content)
13796         };
13797         if(name){
13798             st[name] = st[subIndex];
13799         }
13800         st[subIndex].tpl.compile();
13801         st[subIndex].tpl.call = this.call.createDelegate(this);
13802         subIndex++;
13803     }
13804     this.subCount = subIndex;
13805     this.subs = st;
13806 };
13807 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13808     /**
13809     * The regular expression used to match sub templates
13810     * @type RegExp
13811     * @property
13812     */
13813     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13814
13815     /**
13816      * Applies the passed values to a child template.
13817      * @param {String/Number} name (optional) The name or index of the child template
13818      * @param {Array/Object} values The values to be applied to the template
13819      * @return {MasterTemplate} this
13820      */
13821      add : function(name, values){
13822         if(arguments.length == 1){
13823             values = arguments[0];
13824             name = 0;
13825         }
13826         var s = this.subs[name];
13827         s.buffer[s.buffer.length] = s.tpl.apply(values);
13828         return this;
13829     },
13830
13831     /**
13832      * Applies all the passed values to a child template.
13833      * @param {String/Number} name (optional) The name or index of the child template
13834      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13835      * @param {Boolean} reset (optional) True to reset the template first
13836      * @return {MasterTemplate} this
13837      */
13838     fill : function(name, values, reset){
13839         var a = arguments;
13840         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13841             values = a[0];
13842             name = 0;
13843             reset = a[1];
13844         }
13845         if(reset){
13846             this.reset();
13847         }
13848         for(var i = 0, len = values.length; i < len; i++){
13849             this.add(name, values[i]);
13850         }
13851         return this;
13852     },
13853
13854     /**
13855      * Resets the template for reuse
13856      * @return {MasterTemplate} this
13857      */
13858      reset : function(){
13859         var s = this.subs;
13860         for(var i = 0; i < this.subCount; i++){
13861             s[i].buffer = [];
13862         }
13863         return this;
13864     },
13865
13866     applyTemplate : function(values){
13867         var s = this.subs;
13868         var replaceIndex = -1;
13869         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13870             return s[++replaceIndex].buffer.join("");
13871         });
13872         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13873     },
13874
13875     apply : function(){
13876         return this.applyTemplate.apply(this, arguments);
13877     },
13878
13879     compile : function(){return this;}
13880 });
13881
13882 /**
13883  * Alias for fill().
13884  * @method
13885  */
13886 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13887  /**
13888  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13889  * var tpl = Roo.MasterTemplate.from('element-id');
13890  * @param {String/HTMLElement} el
13891  * @param {Object} config
13892  * @static
13893  */
13894 Roo.MasterTemplate.from = function(el, config){
13895     el = Roo.getDom(el);
13896     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13897 };/*
13898  * Based on:
13899  * Ext JS Library 1.1.1
13900  * Copyright(c) 2006-2007, Ext JS, LLC.
13901  *
13902  * Originally Released Under LGPL - original licence link has changed is not relivant.
13903  *
13904  * Fork - LGPL
13905  * <script type="text/javascript">
13906  */
13907
13908  
13909 /**
13910  * @class Roo.util.CSS
13911  * Utility class for manipulating CSS rules
13912  * @singleton
13913  */
13914 Roo.util.CSS = function(){
13915         var rules = null;
13916         var doc = document;
13917
13918     var camelRe = /(-[a-z])/gi;
13919     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13920
13921    return {
13922    /**
13923     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13924     * tag and appended to the HEAD of the document.
13925     * @param {String|Object} cssText The text containing the css rules
13926     * @param {String} id An id to add to the stylesheet for later removal
13927     * @return {StyleSheet}
13928     */
13929     createStyleSheet : function(cssText, id){
13930         var ss;
13931         var head = doc.getElementsByTagName("head")[0];
13932         var nrules = doc.createElement("style");
13933         nrules.setAttribute("type", "text/css");
13934         if(id){
13935             nrules.setAttribute("id", id);
13936         }
13937         if (typeof(cssText) != 'string') {
13938             // support object maps..
13939             // not sure if this a good idea.. 
13940             // perhaps it should be merged with the general css handling
13941             // and handle js style props.
13942             var cssTextNew = [];
13943             for(var n in cssText) {
13944                 var citems = [];
13945                 for(var k in cssText[n]) {
13946                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13947                 }
13948                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13949                 
13950             }
13951             cssText = cssTextNew.join("\n");
13952             
13953         }
13954        
13955        
13956        if(Roo.isIE){
13957            head.appendChild(nrules);
13958            ss = nrules.styleSheet;
13959            ss.cssText = cssText;
13960        }else{
13961            try{
13962                 nrules.appendChild(doc.createTextNode(cssText));
13963            }catch(e){
13964                nrules.cssText = cssText; 
13965            }
13966            head.appendChild(nrules);
13967            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13968        }
13969        this.cacheStyleSheet(ss);
13970        return ss;
13971    },
13972
13973    /**
13974     * Removes a style or link tag by id
13975     * @param {String} id The id of the tag
13976     */
13977    removeStyleSheet : function(id){
13978        var existing = doc.getElementById(id);
13979        if(existing){
13980            existing.parentNode.removeChild(existing);
13981        }
13982    },
13983
13984    /**
13985     * Dynamically swaps an existing stylesheet reference for a new one
13986     * @param {String} id The id of an existing link tag to remove
13987     * @param {String} url The href of the new stylesheet to include
13988     */
13989    swapStyleSheet : function(id, url){
13990        this.removeStyleSheet(id);
13991        var ss = doc.createElement("link");
13992        ss.setAttribute("rel", "stylesheet");
13993        ss.setAttribute("type", "text/css");
13994        ss.setAttribute("id", id);
13995        ss.setAttribute("href", url);
13996        doc.getElementsByTagName("head")[0].appendChild(ss);
13997    },
13998    
13999    /**
14000     * Refresh the rule cache if you have dynamically added stylesheets
14001     * @return {Object} An object (hash) of rules indexed by selector
14002     */
14003    refreshCache : function(){
14004        return this.getRules(true);
14005    },
14006
14007    // private
14008    cacheStyleSheet : function(stylesheet){
14009        if(!rules){
14010            rules = {};
14011        }
14012        try{// try catch for cross domain access issue
14013            var ssRules = stylesheet.cssRules || stylesheet.rules;
14014            for(var j = ssRules.length-1; j >= 0; --j){
14015                rules[ssRules[j].selectorText] = ssRules[j];
14016            }
14017        }catch(e){}
14018    },
14019    
14020    /**
14021     * Gets all css rules for the document
14022     * @param {Boolean} refreshCache true to refresh the internal cache
14023     * @return {Object} An object (hash) of rules indexed by selector
14024     */
14025    getRules : function(refreshCache){
14026                 if(rules == null || refreshCache){
14027                         rules = {};
14028                         var ds = doc.styleSheets;
14029                         for(var i =0, len = ds.length; i < len; i++){
14030                             try{
14031                         this.cacheStyleSheet(ds[i]);
14032                     }catch(e){} 
14033                 }
14034                 }
14035                 return rules;
14036         },
14037         
14038         /**
14039     * Gets an an individual CSS rule by selector(s)
14040     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14041     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14042     * @return {CSSRule} The CSS rule or null if one is not found
14043     */
14044    getRule : function(selector, refreshCache){
14045                 var rs = this.getRules(refreshCache);
14046                 if(!(selector instanceof Array)){
14047                     return rs[selector];
14048                 }
14049                 for(var i = 0; i < selector.length; i++){
14050                         if(rs[selector[i]]){
14051                                 return rs[selector[i]];
14052                         }
14053                 }
14054                 return null;
14055         },
14056         
14057         
14058         /**
14059     * Updates a rule property
14060     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14061     * @param {String} property The css property
14062     * @param {String} value The new value for the property
14063     * @return {Boolean} true If a rule was found and updated
14064     */
14065    updateRule : function(selector, property, value){
14066                 if(!(selector instanceof Array)){
14067                         var rule = this.getRule(selector);
14068                         if(rule){
14069                                 rule.style[property.replace(camelRe, camelFn)] = value;
14070                                 return true;
14071                         }
14072                 }else{
14073                         for(var i = 0; i < selector.length; i++){
14074                                 if(this.updateRule(selector[i], property, value)){
14075                                         return true;
14076                                 }
14077                         }
14078                 }
14079                 return false;
14080         }
14081    };   
14082 }();/*
14083  * Based on:
14084  * Ext JS Library 1.1.1
14085  * Copyright(c) 2006-2007, Ext JS, LLC.
14086  *
14087  * Originally Released Under LGPL - original licence link has changed is not relivant.
14088  *
14089  * Fork - LGPL
14090  * <script type="text/javascript">
14091  */
14092
14093  
14094
14095 /**
14096  * @class Roo.util.ClickRepeater
14097  * @extends Roo.util.Observable
14098  * 
14099  * A wrapper class which can be applied to any element. Fires a "click" event while the
14100  * mouse is pressed. The interval between firings may be specified in the config but
14101  * defaults to 10 milliseconds.
14102  * 
14103  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14104  * 
14105  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14106  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14107  * Similar to an autorepeat key delay.
14108  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14109  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14110  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14111  *           "interval" and "delay" are ignored. "immediate" is honored.
14112  * @cfg {Boolean} preventDefault True to prevent the default click event
14113  * @cfg {Boolean} stopDefault True to stop the default click event
14114  * 
14115  * @history
14116  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14117  *     2007-02-02 jvs Renamed to ClickRepeater
14118  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14119  *
14120  *  @constructor
14121  * @param {String/HTMLElement/Element} el The element to listen on
14122  * @param {Object} config
14123  **/
14124 Roo.util.ClickRepeater = function(el, config)
14125 {
14126     this.el = Roo.get(el);
14127     this.el.unselectable();
14128
14129     Roo.apply(this, config);
14130
14131     this.addEvents({
14132     /**
14133      * @event mousedown
14134      * Fires when the mouse button is depressed.
14135      * @param {Roo.util.ClickRepeater} this
14136      */
14137         "mousedown" : true,
14138     /**
14139      * @event click
14140      * Fires on a specified interval during the time the element is pressed.
14141      * @param {Roo.util.ClickRepeater} this
14142      */
14143         "click" : true,
14144     /**
14145      * @event mouseup
14146      * Fires when the mouse key is released.
14147      * @param {Roo.util.ClickRepeater} this
14148      */
14149         "mouseup" : true
14150     });
14151
14152     this.el.on("mousedown", this.handleMouseDown, this);
14153     if(this.preventDefault || this.stopDefault){
14154         this.el.on("click", function(e){
14155             if(this.preventDefault){
14156                 e.preventDefault();
14157             }
14158             if(this.stopDefault){
14159                 e.stopEvent();
14160             }
14161         }, this);
14162     }
14163
14164     // allow inline handler
14165     if(this.handler){
14166         this.on("click", this.handler,  this.scope || this);
14167     }
14168
14169     Roo.util.ClickRepeater.superclass.constructor.call(this);
14170 };
14171
14172 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14173     interval : 20,
14174     delay: 250,
14175     preventDefault : true,
14176     stopDefault : false,
14177     timer : 0,
14178
14179     // private
14180     handleMouseDown : function(){
14181         clearTimeout(this.timer);
14182         this.el.blur();
14183         if(this.pressClass){
14184             this.el.addClass(this.pressClass);
14185         }
14186         this.mousedownTime = new Date();
14187
14188         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14189         this.el.on("mouseout", this.handleMouseOut, this);
14190
14191         this.fireEvent("mousedown", this);
14192         this.fireEvent("click", this);
14193         
14194         this.timer = this.click.defer(this.delay || this.interval, this);
14195     },
14196
14197     // private
14198     click : function(){
14199         this.fireEvent("click", this);
14200         this.timer = this.click.defer(this.getInterval(), this);
14201     },
14202
14203     // private
14204     getInterval: function(){
14205         if(!this.accelerate){
14206             return this.interval;
14207         }
14208         var pressTime = this.mousedownTime.getElapsed();
14209         if(pressTime < 500){
14210             return 400;
14211         }else if(pressTime < 1700){
14212             return 320;
14213         }else if(pressTime < 2600){
14214             return 250;
14215         }else if(pressTime < 3500){
14216             return 180;
14217         }else if(pressTime < 4400){
14218             return 140;
14219         }else if(pressTime < 5300){
14220             return 80;
14221         }else if(pressTime < 6200){
14222             return 50;
14223         }else{
14224             return 10;
14225         }
14226     },
14227
14228     // private
14229     handleMouseOut : function(){
14230         clearTimeout(this.timer);
14231         if(this.pressClass){
14232             this.el.removeClass(this.pressClass);
14233         }
14234         this.el.on("mouseover", this.handleMouseReturn, this);
14235     },
14236
14237     // private
14238     handleMouseReturn : function(){
14239         this.el.un("mouseover", this.handleMouseReturn);
14240         if(this.pressClass){
14241             this.el.addClass(this.pressClass);
14242         }
14243         this.click();
14244     },
14245
14246     // private
14247     handleMouseUp : function(){
14248         clearTimeout(this.timer);
14249         this.el.un("mouseover", this.handleMouseReturn);
14250         this.el.un("mouseout", this.handleMouseOut);
14251         Roo.get(document).un("mouseup", this.handleMouseUp);
14252         this.el.removeClass(this.pressClass);
14253         this.fireEvent("mouseup", this);
14254     }
14255 });/*
14256  * Based on:
14257  * Ext JS Library 1.1.1
14258  * Copyright(c) 2006-2007, Ext JS, LLC.
14259  *
14260  * Originally Released Under LGPL - original licence link has changed is not relivant.
14261  *
14262  * Fork - LGPL
14263  * <script type="text/javascript">
14264  */
14265
14266  
14267 /**
14268  * @class Roo.KeyNav
14269  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14270  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14271  * way to implement custom navigation schemes for any UI component.</p>
14272  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14273  * pageUp, pageDown, del, home, end.  Usage:</p>
14274  <pre><code>
14275 var nav = new Roo.KeyNav("my-element", {
14276     "left" : function(e){
14277         this.moveLeft(e.ctrlKey);
14278     },
14279     "right" : function(e){
14280         this.moveRight(e.ctrlKey);
14281     },
14282     "enter" : function(e){
14283         this.save();
14284     },
14285     scope : this
14286 });
14287 </code></pre>
14288  * @constructor
14289  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14290  * @param {Object} config The config
14291  */
14292 Roo.KeyNav = function(el, config){
14293     this.el = Roo.get(el);
14294     Roo.apply(this, config);
14295     if(!this.disabled){
14296         this.disabled = true;
14297         this.enable();
14298     }
14299 };
14300
14301 Roo.KeyNav.prototype = {
14302     /**
14303      * @cfg {Boolean} disabled
14304      * True to disable this KeyNav instance (defaults to false)
14305      */
14306     disabled : false,
14307     /**
14308      * @cfg {String} defaultEventAction
14309      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14310      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14311      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14312      */
14313     defaultEventAction: "stopEvent",
14314     /**
14315      * @cfg {Boolean} forceKeyDown
14316      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14317      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14318      * handle keydown instead of keypress.
14319      */
14320     forceKeyDown : false,
14321
14322     // private
14323     prepareEvent : function(e){
14324         var k = e.getKey();
14325         var h = this.keyToHandler[k];
14326         //if(h && this[h]){
14327         //    e.stopPropagation();
14328         //}
14329         if(Roo.isSafari && h && k >= 37 && k <= 40){
14330             e.stopEvent();
14331         }
14332     },
14333
14334     // private
14335     relay : function(e){
14336         var k = e.getKey();
14337         var h = this.keyToHandler[k];
14338         if(h && this[h]){
14339             if(this.doRelay(e, this[h], h) !== true){
14340                 e[this.defaultEventAction]();
14341             }
14342         }
14343     },
14344
14345     // private
14346     doRelay : function(e, h, hname){
14347         return h.call(this.scope || this, e);
14348     },
14349
14350     // possible handlers
14351     enter : false,
14352     left : false,
14353     right : false,
14354     up : false,
14355     down : false,
14356     tab : false,
14357     esc : false,
14358     pageUp : false,
14359     pageDown : false,
14360     del : false,
14361     home : false,
14362     end : false,
14363
14364     // quick lookup hash
14365     keyToHandler : {
14366         37 : "left",
14367         39 : "right",
14368         38 : "up",
14369         40 : "down",
14370         33 : "pageUp",
14371         34 : "pageDown",
14372         46 : "del",
14373         36 : "home",
14374         35 : "end",
14375         13 : "enter",
14376         27 : "esc",
14377         9  : "tab"
14378     },
14379
14380         /**
14381          * Enable this KeyNav
14382          */
14383         enable: function(){
14384                 if(this.disabled){
14385             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14386             // the EventObject will normalize Safari automatically
14387             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14388                 this.el.on("keydown", this.relay,  this);
14389             }else{
14390                 this.el.on("keydown", this.prepareEvent,  this);
14391                 this.el.on("keypress", this.relay,  this);
14392             }
14393                     this.disabled = false;
14394                 }
14395         },
14396
14397         /**
14398          * Disable this KeyNav
14399          */
14400         disable: function(){
14401                 if(!this.disabled){
14402                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14403                 this.el.un("keydown", this.relay);
14404             }else{
14405                 this.el.un("keydown", this.prepareEvent);
14406                 this.el.un("keypress", this.relay);
14407             }
14408                     this.disabled = true;
14409                 }
14410         }
14411 };/*
14412  * Based on:
14413  * Ext JS Library 1.1.1
14414  * Copyright(c) 2006-2007, Ext JS, LLC.
14415  *
14416  * Originally Released Under LGPL - original licence link has changed is not relivant.
14417  *
14418  * Fork - LGPL
14419  * <script type="text/javascript">
14420  */
14421
14422  
14423 /**
14424  * @class Roo.KeyMap
14425  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14426  * The constructor accepts the same config object as defined by {@link #addBinding}.
14427  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14428  * combination it will call the function with this signature (if the match is a multi-key
14429  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14430  * A KeyMap can also handle a string representation of keys.<br />
14431  * Usage:
14432  <pre><code>
14433 // map one key by key code
14434 var map = new Roo.KeyMap("my-element", {
14435     key: 13, // or Roo.EventObject.ENTER
14436     fn: myHandler,
14437     scope: myObject
14438 });
14439
14440 // map multiple keys to one action by string
14441 var map = new Roo.KeyMap("my-element", {
14442     key: "a\r\n\t",
14443     fn: myHandler,
14444     scope: myObject
14445 });
14446
14447 // map multiple keys to multiple actions by strings and array of codes
14448 var map = new Roo.KeyMap("my-element", [
14449     {
14450         key: [10,13],
14451         fn: function(){ alert("Return was pressed"); }
14452     }, {
14453         key: "abc",
14454         fn: function(){ alert('a, b or c was pressed'); }
14455     }, {
14456         key: "\t",
14457         ctrl:true,
14458         shift:true,
14459         fn: function(){ alert('Control + shift + tab was pressed.'); }
14460     }
14461 ]);
14462 </code></pre>
14463  * <b>Note: A KeyMap starts enabled</b>
14464  * @constructor
14465  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14466  * @param {Object} config The config (see {@link #addBinding})
14467  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14468  */
14469 Roo.KeyMap = function(el, config, eventName){
14470     this.el  = Roo.get(el);
14471     this.eventName = eventName || "keydown";
14472     this.bindings = [];
14473     if(config){
14474         this.addBinding(config);
14475     }
14476     this.enable();
14477 };
14478
14479 Roo.KeyMap.prototype = {
14480     /**
14481      * True to stop the event from bubbling and prevent the default browser action if the
14482      * key was handled by the KeyMap (defaults to false)
14483      * @type Boolean
14484      */
14485     stopEvent : false,
14486
14487     /**
14488      * Add a new binding to this KeyMap. The following config object properties are supported:
14489      * <pre>
14490 Property    Type             Description
14491 ----------  ---------------  ----------------------------------------------------------------------
14492 key         String/Array     A single keycode or an array of keycodes to handle
14493 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14494 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14495 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14496 fn          Function         The function to call when KeyMap finds the expected key combination
14497 scope       Object           The scope of the callback function
14498 </pre>
14499      *
14500      * Usage:
14501      * <pre><code>
14502 // Create a KeyMap
14503 var map = new Roo.KeyMap(document, {
14504     key: Roo.EventObject.ENTER,
14505     fn: handleKey,
14506     scope: this
14507 });
14508
14509 //Add a new binding to the existing KeyMap later
14510 map.addBinding({
14511     key: 'abc',
14512     shift: true,
14513     fn: handleKey,
14514     scope: this
14515 });
14516 </code></pre>
14517      * @param {Object/Array} config A single KeyMap config or an array of configs
14518      */
14519         addBinding : function(config){
14520         if(config instanceof Array){
14521             for(var i = 0, len = config.length; i < len; i++){
14522                 this.addBinding(config[i]);
14523             }
14524             return;
14525         }
14526         var keyCode = config.key,
14527             shift = config.shift, 
14528             ctrl = config.ctrl, 
14529             alt = config.alt,
14530             fn = config.fn,
14531             scope = config.scope;
14532         if(typeof keyCode == "string"){
14533             var ks = [];
14534             var keyString = keyCode.toUpperCase();
14535             for(var j = 0, len = keyString.length; j < len; j++){
14536                 ks.push(keyString.charCodeAt(j));
14537             }
14538             keyCode = ks;
14539         }
14540         var keyArray = keyCode instanceof Array;
14541         var handler = function(e){
14542             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14543                 var k = e.getKey();
14544                 if(keyArray){
14545                     for(var i = 0, len = keyCode.length; i < len; i++){
14546                         if(keyCode[i] == k){
14547                           if(this.stopEvent){
14548                               e.stopEvent();
14549                           }
14550                           fn.call(scope || window, k, e);
14551                           return;
14552                         }
14553                     }
14554                 }else{
14555                     if(k == keyCode){
14556                         if(this.stopEvent){
14557                            e.stopEvent();
14558                         }
14559                         fn.call(scope || window, k, e);
14560                     }
14561                 }
14562             }
14563         };
14564         this.bindings.push(handler);  
14565         },
14566
14567     /**
14568      * Shorthand for adding a single key listener
14569      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14570      * following options:
14571      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14572      * @param {Function} fn The function to call
14573      * @param {Object} scope (optional) The scope of the function
14574      */
14575     on : function(key, fn, scope){
14576         var keyCode, shift, ctrl, alt;
14577         if(typeof key == "object" && !(key instanceof Array)){
14578             keyCode = key.key;
14579             shift = key.shift;
14580             ctrl = key.ctrl;
14581             alt = key.alt;
14582         }else{
14583             keyCode = key;
14584         }
14585         this.addBinding({
14586             key: keyCode,
14587             shift: shift,
14588             ctrl: ctrl,
14589             alt: alt,
14590             fn: fn,
14591             scope: scope
14592         })
14593     },
14594
14595     // private
14596     handleKeyDown : function(e){
14597             if(this.enabled){ //just in case
14598             var b = this.bindings;
14599             for(var i = 0, len = b.length; i < len; i++){
14600                 b[i].call(this, e);
14601             }
14602             }
14603         },
14604         
14605         /**
14606          * Returns true if this KeyMap is enabled
14607          * @return {Boolean} 
14608          */
14609         isEnabled : function(){
14610             return this.enabled;  
14611         },
14612         
14613         /**
14614          * Enables this KeyMap
14615          */
14616         enable: function(){
14617                 if(!this.enabled){
14618                     this.el.on(this.eventName, this.handleKeyDown, this);
14619                     this.enabled = true;
14620                 }
14621         },
14622
14623         /**
14624          * Disable this KeyMap
14625          */
14626         disable: function(){
14627                 if(this.enabled){
14628                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14629                     this.enabled = false;
14630                 }
14631         }
14632 };/*
14633  * Based on:
14634  * Ext JS Library 1.1.1
14635  * Copyright(c) 2006-2007, Ext JS, LLC.
14636  *
14637  * Originally Released Under LGPL - original licence link has changed is not relivant.
14638  *
14639  * Fork - LGPL
14640  * <script type="text/javascript">
14641  */
14642
14643  
14644 /**
14645  * @class Roo.util.TextMetrics
14646  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14647  * wide, in pixels, a given block of text will be.
14648  * @singleton
14649  */
14650 Roo.util.TextMetrics = function(){
14651     var shared;
14652     return {
14653         /**
14654          * Measures the size of the specified text
14655          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14656          * that can affect the size of the rendered text
14657          * @param {String} text The text to measure
14658          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14659          * in order to accurately measure the text height
14660          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14661          */
14662         measure : function(el, text, fixedWidth){
14663             if(!shared){
14664                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14665             }
14666             shared.bind(el);
14667             shared.setFixedWidth(fixedWidth || 'auto');
14668             return shared.getSize(text);
14669         },
14670
14671         /**
14672          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14673          * the overhead of multiple calls to initialize the style properties on each measurement.
14674          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14675          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14676          * in order to accurately measure the text height
14677          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14678          */
14679         createInstance : function(el, fixedWidth){
14680             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14681         }
14682     };
14683 }();
14684
14685  
14686
14687 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14688     var ml = new Roo.Element(document.createElement('div'));
14689     document.body.appendChild(ml.dom);
14690     ml.position('absolute');
14691     ml.setLeftTop(-1000, -1000);
14692     ml.hide();
14693
14694     if(fixedWidth){
14695         ml.setWidth(fixedWidth);
14696     }
14697      
14698     var instance = {
14699         /**
14700          * Returns the size of the specified text based on the internal element's style and width properties
14701          * @memberOf Roo.util.TextMetrics.Instance#
14702          * @param {String} text The text to measure
14703          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14704          */
14705         getSize : function(text){
14706             ml.update(text);
14707             var s = ml.getSize();
14708             ml.update('');
14709             return s;
14710         },
14711
14712         /**
14713          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14714          * that can affect the size of the rendered text
14715          * @memberOf Roo.util.TextMetrics.Instance#
14716          * @param {String/HTMLElement} el The element, dom node or id
14717          */
14718         bind : function(el){
14719             ml.setStyle(
14720                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14721             );
14722         },
14723
14724         /**
14725          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14726          * to set a fixed width in order to accurately measure the text height.
14727          * @memberOf Roo.util.TextMetrics.Instance#
14728          * @param {Number} width The width to set on the element
14729          */
14730         setFixedWidth : function(width){
14731             ml.setWidth(width);
14732         },
14733
14734         /**
14735          * Returns the measured width of the specified text
14736          * @memberOf Roo.util.TextMetrics.Instance#
14737          * @param {String} text The text to measure
14738          * @return {Number} width The width in pixels
14739          */
14740         getWidth : function(text){
14741             ml.dom.style.width = 'auto';
14742             return this.getSize(text).width;
14743         },
14744
14745         /**
14746          * Returns the measured height of the specified text.  For multiline text, be sure to call
14747          * {@link #setFixedWidth} if necessary.
14748          * @memberOf Roo.util.TextMetrics.Instance#
14749          * @param {String} text The text to measure
14750          * @return {Number} height The height in pixels
14751          */
14752         getHeight : function(text){
14753             return this.getSize(text).height;
14754         }
14755     };
14756
14757     instance.bind(bindTo);
14758
14759     return instance;
14760 };
14761
14762 // backwards compat
14763 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14764  * Based on:
14765  * Ext JS Library 1.1.1
14766  * Copyright(c) 2006-2007, Ext JS, LLC.
14767  *
14768  * Originally Released Under LGPL - original licence link has changed is not relivant.
14769  *
14770  * Fork - LGPL
14771  * <script type="text/javascript">
14772  */
14773
14774 /**
14775  * @class Roo.state.Provider
14776  * Abstract base class for state provider implementations. This class provides methods
14777  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14778  * Provider interface.
14779  */
14780 Roo.state.Provider = function(){
14781     /**
14782      * @event statechange
14783      * Fires when a state change occurs.
14784      * @param {Provider} this This state provider
14785      * @param {String} key The state key which was changed
14786      * @param {String} value The encoded value for the state
14787      */
14788     this.addEvents({
14789         "statechange": true
14790     });
14791     this.state = {};
14792     Roo.state.Provider.superclass.constructor.call(this);
14793 };
14794 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14795     /**
14796      * Returns the current value for a key
14797      * @param {String} name The key name
14798      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14799      * @return {Mixed} The state data
14800      */
14801     get : function(name, defaultValue){
14802         return typeof this.state[name] == "undefined" ?
14803             defaultValue : this.state[name];
14804     },
14805     
14806     /**
14807      * Clears a value from the state
14808      * @param {String} name The key name
14809      */
14810     clear : function(name){
14811         delete this.state[name];
14812         this.fireEvent("statechange", this, name, null);
14813     },
14814     
14815     /**
14816      * Sets the value for a key
14817      * @param {String} name The key name
14818      * @param {Mixed} value The value to set
14819      */
14820     set : function(name, value){
14821         this.state[name] = value;
14822         this.fireEvent("statechange", this, name, value);
14823     },
14824     
14825     /**
14826      * Decodes a string previously encoded with {@link #encodeValue}.
14827      * @param {String} value The value to decode
14828      * @return {Mixed} The decoded value
14829      */
14830     decodeValue : function(cookie){
14831         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14832         var matches = re.exec(unescape(cookie));
14833         if(!matches || !matches[1]) return; // non state cookie
14834         var type = matches[1];
14835         var v = matches[2];
14836         switch(type){
14837             case "n":
14838                 return parseFloat(v);
14839             case "d":
14840                 return new Date(Date.parse(v));
14841             case "b":
14842                 return (v == "1");
14843             case "a":
14844                 var all = [];
14845                 var values = v.split("^");
14846                 for(var i = 0, len = values.length; i < len; i++){
14847                     all.push(this.decodeValue(values[i]));
14848                 }
14849                 return all;
14850            case "o":
14851                 var all = {};
14852                 var values = v.split("^");
14853                 for(var i = 0, len = values.length; i < len; i++){
14854                     var kv = values[i].split("=");
14855                     all[kv[0]] = this.decodeValue(kv[1]);
14856                 }
14857                 return all;
14858            default:
14859                 return v;
14860         }
14861     },
14862     
14863     /**
14864      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14865      * @param {Mixed} value The value to encode
14866      * @return {String} The encoded value
14867      */
14868     encodeValue : function(v){
14869         var enc;
14870         if(typeof v == "number"){
14871             enc = "n:" + v;
14872         }else if(typeof v == "boolean"){
14873             enc = "b:" + (v ? "1" : "0");
14874         }else if(v instanceof Date){
14875             enc = "d:" + v.toGMTString();
14876         }else if(v instanceof Array){
14877             var flat = "";
14878             for(var i = 0, len = v.length; i < len; i++){
14879                 flat += this.encodeValue(v[i]);
14880                 if(i != len-1) flat += "^";
14881             }
14882             enc = "a:" + flat;
14883         }else if(typeof v == "object"){
14884             var flat = "";
14885             for(var key in v){
14886                 if(typeof v[key] != "function"){
14887                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14888                 }
14889             }
14890             enc = "o:" + flat.substring(0, flat.length-1);
14891         }else{
14892             enc = "s:" + v;
14893         }
14894         return escape(enc);        
14895     }
14896 });
14897
14898 /*
14899  * Based on:
14900  * Ext JS Library 1.1.1
14901  * Copyright(c) 2006-2007, Ext JS, LLC.
14902  *
14903  * Originally Released Under LGPL - original licence link has changed is not relivant.
14904  *
14905  * Fork - LGPL
14906  * <script type="text/javascript">
14907  */
14908 /**
14909  * @class Roo.state.Manager
14910  * This is the global state manager. By default all components that are "state aware" check this class
14911  * for state information if you don't pass them a custom state provider. In order for this class
14912  * to be useful, it must be initialized with a provider when your application initializes.
14913  <pre><code>
14914 // in your initialization function
14915 init : function(){
14916    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14917    ...
14918    // supposed you have a {@link Roo.BorderLayout}
14919    var layout = new Roo.BorderLayout(...);
14920    layout.restoreState();
14921    // or a {Roo.BasicDialog}
14922    var dialog = new Roo.BasicDialog(...);
14923    dialog.restoreState();
14924  </code></pre>
14925  * @singleton
14926  */
14927 Roo.state.Manager = function(){
14928     var provider = new Roo.state.Provider();
14929     
14930     return {
14931         /**
14932          * Configures the default state provider for your application
14933          * @param {Provider} stateProvider The state provider to set
14934          */
14935         setProvider : function(stateProvider){
14936             provider = stateProvider;
14937         },
14938         
14939         /**
14940          * Returns the current value for a key
14941          * @param {String} name The key name
14942          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14943          * @return {Mixed} The state data
14944          */
14945         get : function(key, defaultValue){
14946             return provider.get(key, defaultValue);
14947         },
14948         
14949         /**
14950          * Sets the value for a key
14951          * @param {String} name The key name
14952          * @param {Mixed} value The state data
14953          */
14954          set : function(key, value){
14955             provider.set(key, value);
14956         },
14957         
14958         /**
14959          * Clears a value from the state
14960          * @param {String} name The key name
14961          */
14962         clear : function(key){
14963             provider.clear(key);
14964         },
14965         
14966         /**
14967          * Gets the currently configured state provider
14968          * @return {Provider} The state provider
14969          */
14970         getProvider : function(){
14971             return provider;
14972         }
14973     };
14974 }();
14975 /*
14976  * Based on:
14977  * Ext JS Library 1.1.1
14978  * Copyright(c) 2006-2007, Ext JS, LLC.
14979  *
14980  * Originally Released Under LGPL - original licence link has changed is not relivant.
14981  *
14982  * Fork - LGPL
14983  * <script type="text/javascript">
14984  */
14985 /**
14986  * @class Roo.state.CookieProvider
14987  * @extends Roo.state.Provider
14988  * The default Provider implementation which saves state via cookies.
14989  * <br />Usage:
14990  <pre><code>
14991    var cp = new Roo.state.CookieProvider({
14992        path: "/cgi-bin/",
14993        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14994        domain: "roojs.com"
14995    })
14996    Roo.state.Manager.setProvider(cp);
14997  </code></pre>
14998  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14999  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15000  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15001  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15002  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15003  * domain the page is running on including the 'www' like 'www.roojs.com')
15004  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15005  * @constructor
15006  * Create a new CookieProvider
15007  * @param {Object} config The configuration object
15008  */
15009 Roo.state.CookieProvider = function(config){
15010     Roo.state.CookieProvider.superclass.constructor.call(this);
15011     this.path = "/";
15012     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15013     this.domain = null;
15014     this.secure = false;
15015     Roo.apply(this, config);
15016     this.state = this.readCookies();
15017 };
15018
15019 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15020     // private
15021     set : function(name, value){
15022         if(typeof value == "undefined" || value === null){
15023             this.clear(name);
15024             return;
15025         }
15026         this.setCookie(name, value);
15027         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15028     },
15029
15030     // private
15031     clear : function(name){
15032         this.clearCookie(name);
15033         Roo.state.CookieProvider.superclass.clear.call(this, name);
15034     },
15035
15036     // private
15037     readCookies : function(){
15038         var cookies = {};
15039         var c = document.cookie + ";";
15040         var re = /\s?(.*?)=(.*?);/g;
15041         var matches;
15042         while((matches = re.exec(c)) != null){
15043             var name = matches[1];
15044             var value = matches[2];
15045             if(name && name.substring(0,3) == "ys-"){
15046                 cookies[name.substr(3)] = this.decodeValue(value);
15047             }
15048         }
15049         return cookies;
15050     },
15051
15052     // private
15053     setCookie : function(name, value){
15054         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15055            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15056            ((this.path == null) ? "" : ("; path=" + this.path)) +
15057            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15058            ((this.secure == true) ? "; secure" : "");
15059     },
15060
15061     // private
15062     clearCookie : function(name){
15063         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15064            ((this.path == null) ? "" : ("; path=" + this.path)) +
15065            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15066            ((this.secure == true) ? "; secure" : "");
15067     }
15068 });/*
15069  * Based on:
15070  * Ext JS Library 1.1.1
15071  * Copyright(c) 2006-2007, Ext JS, LLC.
15072  *
15073  * Originally Released Under LGPL - original licence link has changed is not relivant.
15074  *
15075  * Fork - LGPL
15076  * <script type="text/javascript">
15077  */
15078
15079
15080
15081 /*
15082  * These classes are derivatives of the similarly named classes in the YUI Library.
15083  * The original license:
15084  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
15085  * Code licensed under the BSD License:
15086  * http://developer.yahoo.net/yui/license.txt
15087  */
15088
15089 (function() {
15090
15091 var Event=Roo.EventManager;
15092 var Dom=Roo.lib.Dom;
15093
15094 /**
15095  * @class Roo.dd.DragDrop
15096  * @extends Roo.util.Observable
15097  * Defines the interface and base operation of items that that can be
15098  * dragged or can be drop targets.  It was designed to be extended, overriding
15099  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
15100  * Up to three html elements can be associated with a DragDrop instance:
15101  * <ul>
15102  * <li>linked element: the element that is passed into the constructor.
15103  * This is the element which defines the boundaries for interaction with
15104  * other DragDrop objects.</li>
15105  * <li>handle element(s): The drag operation only occurs if the element that
15106  * was clicked matches a handle element.  By default this is the linked
15107  * element, but there are times that you will want only a portion of the
15108  * linked element to initiate the drag operation, and the setHandleElId()
15109  * method provides a way to define this.</li>
15110  * <li>drag element: this represents the element that would be moved along
15111  * with the cursor during a drag operation.  By default, this is the linked
15112  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
15113  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
15114  * </li>
15115  * </ul>
15116  * This class should not be instantiated until the onload event to ensure that
15117  * the associated elements are available.
15118  * The following would define a DragDrop obj that would interact with any
15119  * other DragDrop obj in the "group1" group:
15120  * <pre>
15121  *  dd = new Roo.dd.DragDrop("div1", "group1");
15122  * </pre>
15123  * Since none of the event handlers have been implemented, nothing would
15124  * actually happen if you were to run the code above.  Normally you would
15125  * override this class or one of the default implementations, but you can
15126  * also override the methods you want on an instance of the class...
15127  * <pre>
15128  *  dd.onDragDrop = function(e, id) {
15129  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
15130  *  }
15131  * </pre>
15132  * @constructor
15133  * @param {String} id of the element that is linked to this instance
15134  * @param {String} sGroup the group of related DragDrop objects
15135  * @param {object} config an object containing configurable attributes
15136  *                Valid properties for DragDrop:
15137  *                    padding, isTarget, maintainOffset, primaryButtonOnly
15138  */
15139 Roo.dd.DragDrop = function(id, sGroup, config) {
15140     if (id) {
15141         this.init(id, sGroup, config);
15142     }
15143     
15144 };
15145
15146 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
15147
15148     /**
15149      * The id of the element associated with this object.  This is what we
15150      * refer to as the "linked element" because the size and position of
15151      * this element is used to determine when the drag and drop objects have
15152      * interacted.
15153      * @property id
15154      * @type String
15155      */
15156     id: null,
15157
15158     /**
15159      * Configuration attributes passed into the constructor
15160      * @property config
15161      * @type object
15162      */
15163     config: null,
15164
15165     /**
15166      * The id of the element that will be dragged.  By default this is same
15167      * as the linked element , but could be changed to another element. Ex:
15168      * Roo.dd.DDProxy
15169      * @property dragElId
15170      * @type String
15171      * @private
15172      */
15173     dragElId: null,
15174
15175     /**
15176      * the id of the element that initiates the drag operation.  By default
15177      * this is the linked element, but could be changed to be a child of this
15178      * element.  This lets us do things like only starting the drag when the
15179      * header element within the linked html element is clicked.
15180      * @property handleElId
15181      * @type String
15182      * @private
15183      */
15184     handleElId: null,
15185
15186     /**
15187      * An associative array of HTML tags that will be ignored if clicked.
15188      * @property invalidHandleTypes
15189      * @type {string: string}
15190      */
15191     invalidHandleTypes: null,
15192
15193     /**
15194      * An associative array of ids for elements that will be ignored if clicked
15195      * @property invalidHandleIds
15196      * @type {string: string}
15197      */
15198     invalidHandleIds: null,
15199
15200     /**
15201      * An indexted array of css class names for elements that will be ignored
15202      * if clicked.
15203      * @property invalidHandleClasses
15204      * @type string[]
15205      */
15206     invalidHandleClasses: null,
15207
15208     /**
15209      * The linked element's absolute X position at the time the drag was
15210      * started
15211      * @property startPageX
15212      * @type int
15213      * @private
15214      */
15215     startPageX: 0,
15216
15217     /**
15218      * The linked element's absolute X position at the time the drag was
15219      * started
15220      * @property startPageY
15221      * @type int
15222      * @private
15223      */
15224     startPageY: 0,
15225
15226     /**
15227      * The group defines a logical collection of DragDrop objects that are
15228      * related.  Instances only get events when interacting with other
15229      * DragDrop object in the same group.  This lets us define multiple
15230      * groups using a single DragDrop subclass if we want.
15231      * @property groups
15232      * @type {string: string}
15233      */
15234     groups: null,
15235
15236     /**
15237      * Individual drag/drop instances can be locked.  This will prevent
15238      * onmousedown start drag.
15239      * @property locked
15240      * @type boolean
15241      * @private
15242      */
15243     locked: false,
15244
15245     /**
15246      * Lock this instance
15247      * @method lock
15248      */
15249     lock: function() { this.locked = true; },
15250
15251     /**
15252      * Unlock this instace
15253      * @method unlock
15254      */
15255     unlock: function() { this.locked = false; },
15256
15257     /**
15258      * By default, all insances can be a drop target.  This can be disabled by
15259      * setting isTarget to false.
15260      * @method isTarget
15261      * @type boolean
15262      */
15263     isTarget: true,
15264
15265     /**
15266      * The padding configured for this drag and drop object for calculating
15267      * the drop zone intersection with this object.
15268      * @method padding
15269      * @type int[]
15270      */
15271     padding: null,
15272
15273     /**
15274      * Cached reference to the linked element
15275      * @property _domRef
15276      * @private
15277      */
15278     _domRef: null,
15279
15280     /**
15281      * Internal typeof flag
15282      * @property __ygDragDrop
15283      * @private
15284      */
15285     __ygDragDrop: true,
15286
15287     /**
15288      * Set to true when horizontal contraints are applied
15289      * @property constrainX
15290      * @type boolean
15291      * @private
15292      */
15293     constrainX: false,
15294
15295     /**
15296      * Set to true when vertical contraints are applied
15297      * @property constrainY
15298      * @type boolean
15299      * @private
15300      */
15301     constrainY: false,
15302
15303     /**
15304      * The left constraint
15305      * @property minX
15306      * @type int
15307      * @private
15308      */
15309     minX: 0,
15310
15311     /**
15312      * The right constraint
15313      * @property maxX
15314      * @type int
15315      * @private
15316      */
15317     maxX: 0,
15318
15319     /**
15320      * The up constraint
15321      * @property minY
15322      * @type int
15323      * @type int
15324      * @private
15325      */
15326     minY: 0,
15327
15328     /**
15329      * The down constraint
15330      * @property maxY
15331      * @type int
15332      * @private
15333      */
15334     maxY: 0,
15335
15336     /**
15337      * Maintain offsets when we resetconstraints.  Set to true when you want
15338      * the position of the element relative to its parent to stay the same
15339      * when the page changes
15340      *
15341      * @property maintainOffset
15342      * @type boolean
15343      */
15344     maintainOffset: false,
15345
15346     /**
15347      * Array of pixel locations the element will snap to if we specified a
15348      * horizontal graduation/interval.  This array is generated automatically
15349      * when you define a tick interval.
15350      * @property xTicks
15351      * @type int[]
15352      */
15353     xTicks: null,
15354
15355     /**
15356      * Array of pixel locations the element will snap to if we specified a
15357      * vertical graduation/interval.  This array is generated automatically
15358      * when you define a tick interval.
15359      * @property yTicks
15360      * @type int[]
15361      */
15362     yTicks: null,
15363
15364     /**
15365      * By default the drag and drop instance will only respond to the primary
15366      * button click (left button for a right-handed mouse).  Set to true to
15367      * allow drag and drop to start with any mouse click that is propogated
15368      * by the browser
15369      * @property primaryButtonOnly
15370      * @type boolean
15371      */
15372     primaryButtonOnly: true,
15373
15374     /**
15375      * The availabe property is false until the linked dom element is accessible.
15376      * @property available
15377      * @type boolean
15378      */
15379     available: false,
15380
15381     /**
15382      * By default, drags can only be initiated if the mousedown occurs in the
15383      * region the linked element is.  This is done in part to work around a
15384      * bug in some browsers that mis-report the mousedown if the previous
15385      * mouseup happened outside of the window.  This property is set to true
15386      * if outer handles are defined.
15387      *
15388      * @property hasOuterHandles
15389      * @type boolean
15390      * @default false
15391      */
15392     hasOuterHandles: false,
15393
15394     /**
15395      * Code that executes immediately before the startDrag event
15396      * @method b4StartDrag
15397      * @private
15398      */
15399     b4StartDrag: function(x, y) { },
15400
15401     /**
15402      * Abstract method called after a drag/drop object is clicked
15403      * and the drag or mousedown time thresholds have beeen met.
15404      * @method startDrag
15405      * @param {int} X click location
15406      * @param {int} Y click location
15407      */
15408     startDrag: function(x, y) { /* override this */ },
15409
15410     /**
15411      * Code that executes immediately before the onDrag event
15412      * @method b4Drag
15413      * @private
15414      */
15415     b4Drag: function(e) { },
15416
15417     /**
15418      * Abstract method called during the onMouseMove event while dragging an
15419      * object.
15420      * @method onDrag
15421      * @param {Event} e the mousemove event
15422      */
15423     onDrag: function(e) { /* override this */ },
15424
15425     /**
15426      * Abstract method called when this element fist begins hovering over
15427      * another DragDrop obj
15428      * @method onDragEnter
15429      * @param {Event} e the mousemove event
15430      * @param {String|DragDrop[]} id In POINT mode, the element
15431      * id this is hovering over.  In INTERSECT mode, an array of one or more
15432      * dragdrop items being hovered over.
15433      */
15434     onDragEnter: function(e, id) { /* override this */ },
15435
15436     /**
15437      * Code that executes immediately before the onDragOver event
15438      * @method b4DragOver
15439      * @private
15440      */
15441     b4DragOver: function(e) { },
15442
15443     /**
15444      * Abstract method called when this element is hovering over another
15445      * DragDrop obj
15446      * @method onDragOver
15447      * @param {Event} e the mousemove event
15448      * @param {String|DragDrop[]} id In POINT mode, the element
15449      * id this is hovering over.  In INTERSECT mode, an array of dd items
15450      * being hovered over.
15451      */
15452     onDragOver: function(e, id) { /* override this */ },
15453
15454     /**
15455      * Code that executes immediately before the onDragOut event
15456      * @method b4DragOut
15457      * @private
15458      */
15459     b4DragOut: function(e) { },
15460
15461     /**
15462      * Abstract method called when we are no longer hovering over an element
15463      * @method onDragOut
15464      * @param {Event} e the mousemove event
15465      * @param {String|DragDrop[]} id In POINT mode, the element
15466      * id this was hovering over.  In INTERSECT mode, an array of dd items
15467      * that the mouse is no longer over.
15468      */
15469     onDragOut: function(e, id) { /* override this */ },
15470
15471     /**
15472      * Code that executes immediately before the onDragDrop event
15473      * @method b4DragDrop
15474      * @private
15475      */
15476     b4DragDrop: function(e) { },
15477
15478     /**
15479      * Abstract method called when this item is dropped on another DragDrop
15480      * obj
15481      * @method onDragDrop
15482      * @param {Event} e the mouseup event
15483      * @param {String|DragDrop[]} id In POINT mode, the element
15484      * id this was dropped on.  In INTERSECT mode, an array of dd items this
15485      * was dropped on.
15486      */
15487     onDragDrop: function(e, id) { /* override this */ },
15488
15489     /**
15490      * Abstract method called when this item is dropped on an area with no
15491      * drop target
15492      * @method onInvalidDrop
15493      * @param {Event} e the mouseup event
15494      */
15495     onInvalidDrop: function(e) { /* override this */ },
15496
15497     /**
15498      * Code that executes immediately before the endDrag event
15499      * @method b4EndDrag
15500      * @private
15501      */
15502     b4EndDrag: function(e) { },
15503
15504     /**
15505      * Fired when we are done dragging the object
15506      * @method endDrag
15507      * @param {Event} e the mouseup event
15508      */
15509     endDrag: function(e) { /* override this */ },
15510
15511     /**
15512      * Code executed immediately before the onMouseDown event
15513      * @method b4MouseDown
15514      * @param {Event} e the mousedown event
15515      * @private
15516      */
15517     b4MouseDown: function(e) {  },
15518
15519     /**
15520      * Event handler that fires when a drag/drop obj gets a mousedown
15521      * @method onMouseDown
15522      * @param {Event} e the mousedown event
15523      */
15524     onMouseDown: function(e) { /* override this */ },
15525
15526     /**
15527      * Event handler that fires when a drag/drop obj gets a mouseup
15528      * @method onMouseUp
15529      * @param {Event} e the mouseup event
15530      */
15531     onMouseUp: function(e) { /* override this */ },
15532
15533     /**
15534      * Override the onAvailable method to do what is needed after the initial
15535      * position was determined.
15536      * @method onAvailable
15537      */
15538     onAvailable: function () {
15539     },
15540
15541     /*
15542      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
15543      * @type Object
15544      */
15545     defaultPadding : {left:0, right:0, top:0, bottom:0},
15546
15547     /*
15548      * Initializes the drag drop object's constraints to restrict movement to a certain element.
15549  *
15550  * Usage:
15551  <pre><code>
15552  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
15553                 { dragElId: "existingProxyDiv" });
15554  dd.startDrag = function(){
15555      this.constrainTo("parent-id");
15556  };
15557  </code></pre>
15558  * Or you can initalize it using the {@link Roo.Element} object:
15559  <pre><code>
15560  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
15561      startDrag : function(){
15562          this.constrainTo("parent-id");
15563      }
15564  });
15565  </code></pre>
15566      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
15567      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
15568      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
15569      * an object containing the sides to pad. For example: {right:10, bottom:10}
15570      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
15571      */
15572     constrainTo : function(constrainTo, pad, inContent){
15573         if(typeof pad == "number"){
15574             pad = {left: pad, right:pad, top:pad, bottom:pad};
15575         }
15576         pad = pad || this.defaultPadding;
15577         var b = Roo.get(this.getEl()).getBox();
15578         var ce = Roo.get(constrainTo);
15579         var s = ce.getScroll();
15580         var c, cd = ce.dom;
15581         if(cd == document.body){
15582             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15583         }else{
15584             xy = ce.getXY();
15585             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15586         }
15587
15588
15589         var topSpace = b.y - c.y;
15590         var leftSpace = b.x - c.x;
15591
15592         this.resetConstraints();
15593         this.setXConstraint(leftSpace - (pad.left||0), // left
15594                 c.width - leftSpace - b.width - (pad.right||0) //right
15595         );
15596         this.setYConstraint(topSpace - (pad.top||0), //top
15597                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15598         );
15599     },
15600
15601     /**
15602      * Returns a reference to the linked element
15603      * @method getEl
15604      * @return {HTMLElement} the html element
15605      */
15606     getEl: function() {
15607         if (!this._domRef) {
15608             this._domRef = Roo.getDom(this.id);
15609         }
15610
15611         return this._domRef;
15612     },
15613
15614     /**
15615      * Returns a reference to the actual element to drag.  By default this is
15616      * the same as the html element, but it can be assigned to another
15617      * element. An example of this can be found in Roo.dd.DDProxy
15618      * @method getDragEl
15619      * @return {HTMLElement} the html element
15620      */
15621     getDragEl: function() {
15622         return Roo.getDom(this.dragElId);
15623     },
15624
15625     /**
15626      * Sets up the DragDrop object.  Must be called in the constructor of any
15627      * Roo.dd.DragDrop subclass
15628      * @method init
15629      * @param id the id of the linked element
15630      * @param {String} sGroup the group of related items
15631      * @param {object} config configuration attributes
15632      */
15633     init: function(id, sGroup, config) {
15634         this.initTarget(id, sGroup, config);
15635         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15636         // Event.on(this.id, "selectstart", Event.preventDefault);
15637     },
15638
15639     /**
15640      * Initializes Targeting functionality only... the object does not
15641      * get a mousedown handler.
15642      * @method initTarget
15643      * @param id the id of the linked element
15644      * @param {String} sGroup the group of related items
15645      * @param {object} config configuration attributes
15646      */
15647     initTarget: function(id, sGroup, config) {
15648
15649         // configuration attributes
15650         this.config = config || {};
15651
15652         // create a local reference to the drag and drop manager
15653         this.DDM = Roo.dd.DDM;
15654         // initialize the groups array
15655         this.groups = {};
15656
15657         // assume that we have an element reference instead of an id if the
15658         // parameter is not a string
15659         if (typeof id !== "string") {
15660             id = Roo.id(id);
15661         }
15662
15663         // set the id
15664         this.id = id;
15665
15666         // add to an interaction group
15667         this.addToGroup((sGroup) ? sGroup : "default");
15668
15669         // We don't want to register this as the handle with the manager
15670         // so we just set the id rather than calling the setter.
15671         this.handleElId = id;
15672
15673         // the linked element is the element that gets dragged by default
15674         this.setDragElId(id);
15675
15676         // by default, clicked anchors will not start drag operations.
15677         this.invalidHandleTypes = { A: "A" };
15678         this.invalidHandleIds = {};
15679         this.invalidHandleClasses = [];
15680
15681         this.applyConfig();
15682
15683         this.handleOnAvailable();
15684     },
15685
15686     /**
15687      * Applies the configuration parameters that were passed into the constructor.
15688      * This is supposed to happen at each level through the inheritance chain.  So
15689      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15690      * DragDrop in order to get all of the parameters that are available in
15691      * each object.
15692      * @method applyConfig
15693      */
15694     applyConfig: function() {
15695
15696         // configurable properties:
15697         //    padding, isTarget, maintainOffset, primaryButtonOnly
15698         this.padding           = this.config.padding || [0, 0, 0, 0];
15699         this.isTarget          = (this.config.isTarget !== false);
15700         this.maintainOffset    = (this.config.maintainOffset);
15701         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15702
15703     },
15704
15705     /**
15706      * Executed when the linked element is available
15707      * @method handleOnAvailable
15708      * @private
15709      */
15710     handleOnAvailable: function() {
15711         this.available = true;
15712         this.resetConstraints();
15713         this.onAvailable();
15714     },
15715
15716      /**
15717      * Configures the padding for the target zone in px.  Effectively expands
15718      * (or reduces) the virtual object size for targeting calculations.
15719      * Supports css-style shorthand; if only one parameter is passed, all sides
15720      * will have that padding, and if only two are passed, the top and bottom
15721      * will have the first param, the left and right the second.
15722      * @method setPadding
15723      * @param {int} iTop    Top pad
15724      * @param {int} iRight  Right pad
15725      * @param {int} iBot    Bot pad
15726      * @param {int} iLeft   Left pad
15727      */
15728     setPadding: function(iTop, iRight, iBot, iLeft) {
15729         // this.padding = [iLeft, iRight, iTop, iBot];
15730         if (!iRight && 0 !== iRight) {
15731             this.padding = [iTop, iTop, iTop, iTop];
15732         } else if (!iBot && 0 !== iBot) {
15733             this.padding = [iTop, iRight, iTop, iRight];
15734         } else {
15735             this.padding = [iTop, iRight, iBot, iLeft];
15736         }
15737     },
15738
15739     /**
15740      * Stores the initial placement of the linked element.
15741      * @method setInitialPosition
15742      * @param {int} diffX   the X offset, default 0
15743      * @param {int} diffY   the Y offset, default 0
15744      */
15745     setInitPosition: function(diffX, diffY) {
15746         var el = this.getEl();
15747
15748         if (!this.DDM.verifyEl(el)) {
15749             return;
15750         }
15751
15752         var dx = diffX || 0;
15753         var dy = diffY || 0;
15754
15755         var p = Dom.getXY( el );
15756
15757         this.initPageX = p[0] - dx;
15758         this.initPageY = p[1] - dy;
15759
15760         this.lastPageX = p[0];
15761         this.lastPageY = p[1];
15762
15763
15764         this.setStartPosition(p);
15765     },
15766
15767     /**
15768      * Sets the start position of the element.  This is set when the obj
15769      * is initialized, the reset when a drag is started.
15770      * @method setStartPosition
15771      * @param pos current position (from previous lookup)
15772      * @private
15773      */
15774     setStartPosition: function(pos) {
15775         var p = pos || Dom.getXY( this.getEl() );
15776         this.deltaSetXY = null;
15777
15778         this.startPageX = p[0];
15779         this.startPageY = p[1];
15780     },
15781
15782     /**
15783      * Add this instance to a group of related drag/drop objects.  All
15784      * instances belong to at least one group, and can belong to as many
15785      * groups as needed.
15786      * @method addToGroup
15787      * @param sGroup {string} the name of the group
15788      */
15789     addToGroup: function(sGroup) {
15790         this.groups[sGroup] = true;
15791         this.DDM.regDragDrop(this, sGroup);
15792     },
15793
15794     /**
15795      * Remove's this instance from the supplied interaction group
15796      * @method removeFromGroup
15797      * @param {string}  sGroup  The group to drop
15798      */
15799     removeFromGroup: function(sGroup) {
15800         if (this.groups[sGroup]) {
15801             delete this.groups[sGroup];
15802         }
15803
15804         this.DDM.removeDDFromGroup(this, sGroup);
15805     },
15806
15807     /**
15808      * Allows you to specify that an element other than the linked element
15809      * will be moved with the cursor during a drag
15810      * @method setDragElId
15811      * @param id {string} the id of the element that will be used to initiate the drag
15812      */
15813     setDragElId: function(id) {
15814         this.dragElId = id;
15815     },
15816
15817     /**
15818      * Allows you to specify a child of the linked element that should be
15819      * used to initiate the drag operation.  An example of this would be if
15820      * you have a content div with text and links.  Clicking anywhere in the
15821      * content area would normally start the drag operation.  Use this method
15822      * to specify that an element inside of the content div is the element
15823      * that starts the drag operation.
15824      * @method setHandleElId
15825      * @param id {string} the id of the element that will be used to
15826      * initiate the drag.
15827      */
15828     setHandleElId: function(id) {
15829         if (typeof id !== "string") {
15830             id = Roo.id(id);
15831         }
15832         this.handleElId = id;
15833         this.DDM.regHandle(this.id, id);
15834     },
15835
15836     /**
15837      * Allows you to set an element outside of the linked element as a drag
15838      * handle
15839      * @method setOuterHandleElId
15840      * @param id the id of the element that will be used to initiate the drag
15841      */
15842     setOuterHandleElId: function(id) {
15843         if (typeof id !== "string") {
15844             id = Roo.id(id);
15845         }
15846         Event.on(id, "mousedown",
15847                 this.handleMouseDown, this);
15848         this.setHandleElId(id);
15849
15850         this.hasOuterHandles = true;
15851     },
15852
15853     /**
15854      * Remove all drag and drop hooks for this element
15855      * @method unreg
15856      */
15857     unreg: function() {
15858         Event.un(this.id, "mousedown",
15859                 this.handleMouseDown);
15860         this._domRef = null;
15861         this.DDM._remove(this);
15862     },
15863
15864     destroy : function(){
15865         this.unreg();
15866     },
15867
15868     /**
15869      * Returns true if this instance is locked, or the drag drop mgr is locked
15870      * (meaning that all drag/drop is disabled on the page.)
15871      * @method isLocked
15872      * @return {boolean} true if this obj or all drag/drop is locked, else
15873      * false
15874      */
15875     isLocked: function() {
15876         return (this.DDM.isLocked() || this.locked);
15877     },
15878
15879     /**
15880      * Fired when this object is clicked
15881      * @method handleMouseDown
15882      * @param {Event} e
15883      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15884      * @private
15885      */
15886     handleMouseDown: function(e, oDD){
15887         if (this.primaryButtonOnly && e.button != 0) {
15888             return;
15889         }
15890
15891         if (this.isLocked()) {
15892             return;
15893         }
15894
15895         this.DDM.refreshCache(this.groups);
15896
15897         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15898         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15899         } else {
15900             if (this.clickValidator(e)) {
15901
15902                 // set the initial element position
15903                 this.setStartPosition();
15904
15905
15906                 this.b4MouseDown(e);
15907                 this.onMouseDown(e);
15908
15909                 this.DDM.handleMouseDown(e, this);
15910
15911                 this.DDM.stopEvent(e);
15912             } else {
15913
15914
15915             }
15916         }
15917     },
15918
15919     clickValidator: function(e) {
15920         var target = e.getTarget();
15921         return ( this.isValidHandleChild(target) &&
15922                     (this.id == this.handleElId ||
15923                         this.DDM.handleWasClicked(target, this.id)) );
15924     },
15925
15926     /**
15927      * Allows you to specify a tag name that should not start a drag operation
15928      * when clicked.  This is designed to facilitate embedding links within a
15929      * drag handle that do something other than start the drag.
15930      * @method addInvalidHandleType
15931      * @param {string} tagName the type of element to exclude
15932      */
15933     addInvalidHandleType: function(tagName) {
15934         var type = tagName.toUpperCase();
15935         this.invalidHandleTypes[type] = type;
15936     },
15937
15938     /**
15939      * Lets you to specify an element id for a child of a drag handle
15940      * that should not initiate a drag
15941      * @method addInvalidHandleId
15942      * @param {string} id the element id of the element you wish to ignore
15943      */
15944     addInvalidHandleId: function(id) {
15945         if (typeof id !== "string") {
15946             id = Roo.id(id);
15947         }
15948         this.invalidHandleIds[id] = id;
15949     },
15950
15951     /**
15952      * Lets you specify a css class of elements that will not initiate a drag
15953      * @method addInvalidHandleClass
15954      * @param {string} cssClass the class of the elements you wish to ignore
15955      */
15956     addInvalidHandleClass: function(cssClass) {
15957         this.invalidHandleClasses.push(cssClass);
15958     },
15959
15960     /**
15961      * Unsets an excluded tag name set by addInvalidHandleType
15962      * @method removeInvalidHandleType
15963      * @param {string} tagName the type of element to unexclude
15964      */
15965     removeInvalidHandleType: function(tagName) {
15966         var type = tagName.toUpperCase();
15967         // this.invalidHandleTypes[type] = null;
15968         delete this.invalidHandleTypes[type];
15969     },
15970
15971     /**
15972      * Unsets an invalid handle id
15973      * @method removeInvalidHandleId
15974      * @param {string} id the id of the element to re-enable
15975      */
15976     removeInvalidHandleId: function(id) {
15977         if (typeof id !== "string") {
15978             id = Roo.id(id);
15979         }
15980         delete this.invalidHandleIds[id];
15981     },
15982
15983     /**
15984      * Unsets an invalid css class
15985      * @method removeInvalidHandleClass
15986      * @param {string} cssClass the class of the element(s) you wish to
15987      * re-enable
15988      */
15989     removeInvalidHandleClass: function(cssClass) {
15990         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15991             if (this.invalidHandleClasses[i] == cssClass) {
15992                 delete this.invalidHandleClasses[i];
15993             }
15994         }
15995     },
15996
15997     /**
15998      * Checks the tag exclusion list to see if this click should be ignored
15999      * @method isValidHandleChild
16000      * @param {HTMLElement} node the HTMLElement to evaluate
16001      * @return {boolean} true if this is a valid tag type, false if not
16002      */
16003     isValidHandleChild: function(node) {
16004
16005         var valid = true;
16006         // var n = (node.nodeName == "#text") ? node.parentNode : node;
16007         var nodeName;
16008         try {
16009             nodeName = node.nodeName.toUpperCase();
16010         } catch(e) {
16011             nodeName = node.nodeName;
16012         }
16013         valid = valid && !this.invalidHandleTypes[nodeName];
16014         valid = valid && !this.invalidHandleIds[node.id];
16015
16016         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
16017             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
16018         }
16019
16020
16021         return valid;
16022
16023     },
16024
16025     /**
16026      * Create the array of horizontal tick marks if an interval was specified
16027      * in setXConstraint().
16028      * @method setXTicks
16029      * @private
16030      */
16031     setXTicks: function(iStartX, iTickSize) {
16032         this.xTicks = [];
16033         this.xTickSize = iTickSize;
16034
16035         var tickMap = {};
16036
16037         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
16038             if (!tickMap[i]) {
16039                 this.xTicks[this.xTicks.length] = i;
16040                 tickMap[i] = true;
16041             }
16042         }
16043
16044         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
16045             if (!tickMap[i]) {
16046                 this.xTicks[this.xTicks.length] = i;
16047                 tickMap[i] = true;
16048             }
16049         }
16050
16051         this.xTicks.sort(this.DDM.numericSort) ;
16052     },
16053
16054     /**
16055      * Create the array of vertical tick marks if an interval was specified in
16056      * setYConstraint().
16057      * @method setYTicks
16058      * @private
16059      */
16060     setYTicks: function(iStartY, iTickSize) {
16061         this.yTicks = [];
16062         this.yTickSize = iTickSize;
16063
16064         var tickMap = {};
16065
16066         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
16067             if (!tickMap[i]) {
16068                 this.yTicks[this.yTicks.length] = i;
16069                 tickMap[i] = true;
16070             }
16071         }
16072
16073         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
16074             if (!tickMap[i]) {
16075                 this.yTicks[this.yTicks.length] = i;
16076                 tickMap[i] = true;
16077             }
16078         }
16079
16080         this.yTicks.sort(this.DDM.numericSort) ;
16081     },
16082
16083     /**
16084      * By default, the element can be dragged any place on the screen.  Use
16085      * this method to limit the horizontal travel of the element.  Pass in
16086      * 0,0 for the parameters if you want to lock the drag to the y axis.
16087      * @method setXConstraint
16088      * @param {int} iLeft the number of pixels the element can move to the left
16089      * @param {int} iRight the number of pixels the element can move to the
16090      * right
16091      * @param {int} iTickSize optional parameter for specifying that the
16092      * element
16093      * should move iTickSize pixels at a time.
16094      */
16095     setXConstraint: function(iLeft, iRight, iTickSize) {
16096         this.leftConstraint = iLeft;
16097         this.rightConstraint = iRight;
16098
16099         this.minX = this.initPageX - iLeft;
16100         this.maxX = this.initPageX + iRight;
16101         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
16102
16103         this.constrainX = true;
16104     },
16105
16106     /**
16107      * Clears any constraints applied to this instance.  Also clears ticks
16108      * since they can't exist independent of a constraint at this time.
16109      * @method clearConstraints
16110      */
16111     clearConstraints: function() {
16112         this.constrainX = false;
16113         this.constrainY = false;
16114         this.clearTicks();
16115     },
16116
16117     /**
16118      * Clears any tick interval defined for this instance
16119      * @method clearTicks
16120      */
16121     clearTicks: function() {
16122         this.xTicks = null;
16123         this.yTicks = null;
16124         this.xTickSize = 0;
16125         this.yTickSize = 0;
16126     },
16127
16128     /**
16129      * By default, the element can be dragged any place on the screen.  Set
16130      * this to limit the vertical travel of the element.  Pass in 0,0 for the
16131      * parameters if you want to lock the drag to the x axis.
16132      * @method setYConstraint
16133      * @param {int} iUp the number of pixels the element can move up
16134      * @param {int} iDown the number of pixels the element can move down
16135      * @param {int} iTickSize optional parameter for specifying that the
16136      * element should move iTickSize pixels at a time.
16137      */
16138     setYConstraint: function(iUp, iDown, iTickSize) {
16139         this.topConstraint = iUp;
16140         this.bottomConstraint = iDown;
16141
16142         this.minY = this.initPageY - iUp;
16143         this.maxY = this.initPageY + iDown;
16144         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
16145
16146         this.constrainY = true;
16147
16148     },
16149
16150     /**
16151      * resetConstraints must be called if you manually reposition a dd element.
16152      * @method resetConstraints
16153      * @param {boolean} maintainOffset
16154      */
16155     resetConstraints: function() {
16156
16157
16158         // Maintain offsets if necessary
16159         if (this.initPageX || this.initPageX === 0) {
16160             // figure out how much this thing has moved
16161             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
16162             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
16163
16164             this.setInitPosition(dx, dy);
16165
16166         // This is the first time we have detected the element's position
16167         } else {
16168             this.setInitPosition();
16169         }
16170
16171         if (this.constrainX) {
16172             this.setXConstraint( this.leftConstraint,
16173                                  this.rightConstraint,
16174                                  this.xTickSize        );
16175         }
16176
16177         if (this.constrainY) {
16178             this.setYConstraint( this.topConstraint,
16179                                  this.bottomConstraint,
16180                                  this.yTickSize         );
16181         }
16182     },
16183
16184     /**
16185      * Normally the drag element is moved pixel by pixel, but we can specify
16186      * that it move a number of pixels at a time.  This method resolves the
16187      * location when we have it set up like this.
16188      * @method getTick
16189      * @param {int} val where we want to place the object
16190      * @param {int[]} tickArray sorted array of valid points
16191      * @return {int} the closest tick
16192      * @private
16193      */
16194     getTick: function(val, tickArray) {
16195
16196         if (!tickArray) {
16197             // If tick interval is not defined, it is effectively 1 pixel,
16198             // so we return the value passed to us.
16199             return val;
16200         } else if (tickArray[0] >= val) {
16201             // The value is lower than the first tick, so we return the first
16202             // tick.
16203             return tickArray[0];
16204         } else {
16205             for (var i=0, len=tickArray.length; i<len; ++i) {
16206                 var next = i + 1;
16207                 if (tickArray[next] && tickArray[next] >= val) {
16208                     var diff1 = val - tickArray[i];
16209                     var diff2 = tickArray[next] - val;
16210                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
16211                 }
16212             }
16213
16214             // The value is larger than the last tick, so we return the last
16215             // tick.
16216             return tickArray[tickArray.length - 1];
16217         }
16218     },
16219
16220     /**
16221      * toString method
16222      * @method toString
16223      * @return {string} string representation of the dd obj
16224      */
16225     toString: function() {
16226         return ("DragDrop " + this.id);
16227     }
16228
16229 });
16230
16231 })();
16232 /*
16233  * Based on:
16234  * Ext JS Library 1.1.1
16235  * Copyright(c) 2006-2007, Ext JS, LLC.
16236  *
16237  * Originally Released Under LGPL - original licence link has changed is not relivant.
16238  *
16239  * Fork - LGPL
16240  * <script type="text/javascript">
16241  */
16242
16243
16244 /**
16245  * The drag and drop utility provides a framework for building drag and drop
16246  * applications.  In addition to enabling drag and drop for specific elements,
16247  * the drag and drop elements are tracked by the manager class, and the
16248  * interactions between the various elements are tracked during the drag and
16249  * the implementing code is notified about these important moments.
16250  */
16251
16252 // Only load the library once.  Rewriting the manager class would orphan
16253 // existing drag and drop instances.
16254 if (!Roo.dd.DragDropMgr) {
16255
16256 /**
16257  * @class Roo.dd.DragDropMgr
16258  * DragDropMgr is a singleton that tracks the element interaction for
16259  * all DragDrop items in the window.  Generally, you will not call
16260  * this class directly, but it does have helper methods that could
16261  * be useful in your DragDrop implementations.
16262  * @singleton
16263  */
16264 Roo.dd.DragDropMgr = function() {
16265
16266     var Event = Roo.EventManager;
16267
16268     return {
16269
16270         /**
16271          * Two dimensional Array of registered DragDrop objects.  The first
16272          * dimension is the DragDrop item group, the second the DragDrop
16273          * object.
16274          * @property ids
16275          * @type {string: string}
16276          * @private
16277          * @static
16278          */
16279         ids: {},
16280
16281         /**
16282          * Array of element ids defined as drag handles.  Used to determine
16283          * if the element that generated the mousedown event is actually the
16284          * handle and not the html element itself.
16285          * @property handleIds
16286          * @type {string: string}
16287          * @private
16288          * @static
16289          */
16290         handleIds: {},
16291
16292         /**
16293          * the DragDrop object that is currently being dragged
16294          * @property dragCurrent
16295          * @type DragDrop
16296          * @private
16297          * @static
16298          **/
16299         dragCurrent: null,
16300
16301         /**
16302          * the DragDrop object(s) that are being hovered over
16303          * @property dragOvers
16304          * @type Array
16305          * @private
16306          * @static
16307          */
16308         dragOvers: {},
16309
16310         /**
16311          * the X distance between the cursor and the object being dragged
16312          * @property deltaX
16313          * @type int
16314          * @private
16315          * @static
16316          */
16317         deltaX: 0,
16318
16319         /**
16320          * the Y distance between the cursor and the object being dragged
16321          * @property deltaY
16322          * @type int
16323          * @private
16324          * @static
16325          */
16326         deltaY: 0,
16327
16328         /**
16329          * Flag to determine if we should prevent the default behavior of the
16330          * events we define. By default this is true, but this can be set to
16331          * false if you need the default behavior (not recommended)
16332          * @property preventDefault
16333          * @type boolean
16334          * @static
16335          */
16336         preventDefault: true,
16337
16338         /**
16339          * Flag to determine if we should stop the propagation of the events
16340          * we generate. This is true by default but you may want to set it to
16341          * false if the html element contains other features that require the
16342          * mouse click.
16343          * @property stopPropagation
16344          * @type boolean
16345          * @static
16346          */
16347         stopPropagation: true,
16348
16349         /**
16350          * Internal flag that is set to true when drag and drop has been
16351          * intialized
16352          * @property initialized
16353          * @private
16354          * @static
16355          */
16356         initalized: false,
16357
16358         /**
16359          * All drag and drop can be disabled.
16360          * @property locked
16361          * @private
16362          * @static
16363          */
16364         locked: false,
16365
16366         /**
16367          * Called the first time an element is registered.
16368          * @method init
16369          * @private
16370          * @static
16371          */
16372         init: function() {
16373             this.initialized = true;
16374         },
16375
16376         /**
16377          * In point mode, drag and drop interaction is defined by the
16378          * location of the cursor during the drag/drop
16379          * @property POINT
16380          * @type int
16381          * @static
16382          */
16383         POINT: 0,
16384
16385         /**
16386          * In intersect mode, drag and drop interactio nis defined by the
16387          * overlap of two or more drag and drop objects.
16388          * @property INTERSECT
16389          * @type int
16390          * @static
16391          */
16392         INTERSECT: 1,
16393
16394         /**
16395          * The current drag and drop mode.  Default: POINT
16396          * @property mode
16397          * @type int
16398          * @static
16399          */
16400         mode: 0,
16401
16402         /**
16403          * Runs method on all drag and drop objects
16404          * @method _execOnAll
16405          * @private
16406          * @static
16407          */
16408         _execOnAll: function(sMethod, args) {
16409             for (var i in this.ids) {
16410                 for (var j in this.ids[i]) {
16411                     var oDD = this.ids[i][j];
16412                     if (! this.isTypeOfDD(oDD)) {
16413                         continue;
16414                     }
16415                     oDD[sMethod].apply(oDD, args);
16416                 }
16417             }
16418         },
16419
16420         /**
16421          * Drag and drop initialization.  Sets up the global event handlers
16422          * @method _onLoad
16423          * @private
16424          * @static
16425          */
16426         _onLoad: function() {
16427
16428             this.init();
16429
16430
16431             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
16432             Event.on(document, "mousemove", this.handleMouseMove, this, true);
16433             Event.on(window,   "unload",    this._onUnload, this, true);
16434             Event.on(window,   "resize",    this._onResize, this, true);
16435             // Event.on(window,   "mouseout",    this._test);
16436
16437         },
16438
16439         /**
16440          * Reset constraints on all drag and drop objs
16441          * @method _onResize
16442          * @private
16443          * @static
16444          */
16445         _onResize: function(e) {
16446             this._execOnAll("resetConstraints", []);
16447         },
16448
16449         /**
16450          * Lock all drag and drop functionality
16451          * @method lock
16452          * @static
16453          */
16454         lock: function() { this.locked = true; },
16455
16456         /**
16457          * Unlock all drag and drop functionality
16458          * @method unlock
16459          * @static
16460          */
16461         unlock: function() { this.locked = false; },
16462
16463         /**
16464          * Is drag and drop locked?
16465          * @method isLocked
16466          * @return {boolean} True if drag and drop is locked, false otherwise.
16467          * @static
16468          */
16469         isLocked: function() { return this.locked; },
16470
16471         /**
16472          * Location cache that is set for all drag drop objects when a drag is
16473          * initiated, cleared when the drag is finished.
16474          * @property locationCache
16475          * @private
16476          * @static
16477          */
16478         locationCache: {},
16479
16480         /**
16481          * Set useCache to false if you want to force object the lookup of each
16482          * drag and drop linked element constantly during a drag.
16483          * @property useCache
16484          * @type boolean
16485          * @static
16486          */
16487         useCache: true,
16488
16489         /**
16490          * The number of pixels that the mouse needs to move after the
16491          * mousedown before the drag is initiated.  Default=3;
16492          * @property clickPixelThresh
16493          * @type int
16494          * @static
16495          */
16496         clickPixelThresh: 3,
16497
16498         /**
16499          * The number of milliseconds after the mousedown event to initiate the
16500          * drag if we don't get a mouseup event. Default=1000
16501          * @property clickTimeThresh
16502          * @type int
16503          * @static
16504          */
16505         clickTimeThresh: 350,
16506
16507         /**
16508          * Flag that indicates that either the drag pixel threshold or the
16509          * mousdown time threshold has been met
16510          * @property dragThreshMet
16511          * @type boolean
16512          * @private
16513          * @static
16514          */
16515         dragThreshMet: false,
16516
16517         /**
16518          * Timeout used for the click time threshold
16519          * @property clickTimeout
16520          * @type Object
16521          * @private
16522          * @static
16523          */
16524         clickTimeout: null,
16525
16526         /**
16527          * The X position of the mousedown event stored for later use when a
16528          * drag threshold is met.
16529          * @property startX
16530          * @type int
16531          * @private
16532          * @static
16533          */
16534         startX: 0,
16535
16536         /**
16537          * The Y position of the mousedown event stored for later use when a
16538          * drag threshold is met.
16539          * @property startY
16540          * @type int
16541          * @private
16542          * @static
16543          */
16544         startY: 0,
16545
16546         /**
16547          * Each DragDrop instance must be registered with the DragDropMgr.
16548          * This is executed in DragDrop.init()
16549          * @method regDragDrop
16550          * @param {DragDrop} oDD the DragDrop object to register
16551          * @param {String} sGroup the name of the group this element belongs to
16552          * @static
16553          */
16554         regDragDrop: function(oDD, sGroup) {
16555             if (!this.initialized) { this.init(); }
16556
16557             if (!this.ids[sGroup]) {
16558                 this.ids[sGroup] = {};
16559             }
16560             this.ids[sGroup][oDD.id] = oDD;
16561         },
16562
16563         /**
16564          * Removes the supplied dd instance from the supplied group. Executed
16565          * by DragDrop.removeFromGroup, so don't call this function directly.
16566          * @method removeDDFromGroup
16567          * @private
16568          * @static
16569          */
16570         removeDDFromGroup: function(oDD, sGroup) {
16571             if (!this.ids[sGroup]) {
16572                 this.ids[sGroup] = {};
16573             }
16574
16575             var obj = this.ids[sGroup];
16576             if (obj && obj[oDD.id]) {
16577                 delete obj[oDD.id];
16578             }
16579         },
16580
16581         /**
16582          * Unregisters a drag and drop item.  This is executed in
16583          * DragDrop.unreg, use that method instead of calling this directly.
16584          * @method _remove
16585          * @private
16586          * @static
16587          */
16588         _remove: function(oDD) {
16589             for (var g in oDD.groups) {
16590                 if (g && this.ids[g][oDD.id]) {
16591                     delete this.ids[g][oDD.id];
16592                 }
16593             }
16594             delete this.handleIds[oDD.id];
16595         },
16596
16597         /**
16598          * Each DragDrop handle element must be registered.  This is done
16599          * automatically when executing DragDrop.setHandleElId()
16600          * @method regHandle
16601          * @param {String} sDDId the DragDrop id this element is a handle for
16602          * @param {String} sHandleId the id of the element that is the drag
16603          * handle
16604          * @static
16605          */
16606         regHandle: function(sDDId, sHandleId) {
16607             if (!this.handleIds[sDDId]) {
16608                 this.handleIds[sDDId] = {};
16609             }
16610             this.handleIds[sDDId][sHandleId] = sHandleId;
16611         },
16612
16613         /**
16614          * Utility function to determine if a given element has been
16615          * registered as a drag drop item.
16616          * @method isDragDrop
16617          * @param {String} id the element id to check
16618          * @return {boolean} true if this element is a DragDrop item,
16619          * false otherwise
16620          * @static
16621          */
16622         isDragDrop: function(id) {
16623             return ( this.getDDById(id) ) ? true : false;
16624         },
16625
16626         /**
16627          * Returns the drag and drop instances that are in all groups the
16628          * passed in instance belongs to.
16629          * @method getRelated
16630          * @param {DragDrop} p_oDD the obj to get related data for
16631          * @param {boolean} bTargetsOnly if true, only return targetable objs
16632          * @return {DragDrop[]} the related instances
16633          * @static
16634          */
16635         getRelated: function(p_oDD, bTargetsOnly) {
16636             var oDDs = [];
16637             for (var i in p_oDD.groups) {
16638                 for (j in this.ids[i]) {
16639                     var dd = this.ids[i][j];
16640                     if (! this.isTypeOfDD(dd)) {
16641                         continue;
16642                     }
16643                     if (!bTargetsOnly || dd.isTarget) {
16644                         oDDs[oDDs.length] = dd;
16645                     }
16646                 }
16647             }
16648
16649             return oDDs;
16650         },
16651
16652         /**
16653          * Returns true if the specified dd target is a legal target for
16654          * the specifice drag obj
16655          * @method isLegalTarget
16656          * @param {DragDrop} the drag obj
16657          * @param {DragDrop} the target
16658          * @return {boolean} true if the target is a legal target for the
16659          * dd obj
16660          * @static
16661          */
16662         isLegalTarget: function (oDD, oTargetDD) {
16663             var targets = this.getRelated(oDD, true);
16664             for (var i=0, len=targets.length;i<len;++i) {
16665                 if (targets[i].id == oTargetDD.id) {
16666                     return true;
16667                 }
16668             }
16669
16670             return false;
16671         },
16672
16673         /**
16674          * My goal is to be able to transparently determine if an object is
16675          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16676          * returns "object", oDD.constructor.toString() always returns
16677          * "DragDrop" and not the name of the subclass.  So for now it just
16678          * evaluates a well-known variable in DragDrop.
16679          * @method isTypeOfDD
16680          * @param {Object} the object to evaluate
16681          * @return {boolean} true if typeof oDD = DragDrop
16682          * @static
16683          */
16684         isTypeOfDD: function (oDD) {
16685             return (oDD && oDD.__ygDragDrop);
16686         },
16687
16688         /**
16689          * Utility function to determine if a given element has been
16690          * registered as a drag drop handle for the given Drag Drop object.
16691          * @method isHandle
16692          * @param {String} id the element id to check
16693          * @return {boolean} true if this element is a DragDrop handle, false
16694          * otherwise
16695          * @static
16696          */
16697         isHandle: function(sDDId, sHandleId) {
16698             return ( this.handleIds[sDDId] &&
16699                             this.handleIds[sDDId][sHandleId] );
16700         },
16701
16702         /**
16703          * Returns the DragDrop instance for a given id
16704          * @method getDDById
16705          * @param {String} id the id of the DragDrop object
16706          * @return {DragDrop} the drag drop object, null if it is not found
16707          * @static
16708          */
16709         getDDById: function(id) {
16710             for (var i in this.ids) {
16711                 if (this.ids[i][id]) {
16712                     return this.ids[i][id];
16713                 }
16714             }
16715             return null;
16716         },
16717
16718         /**
16719          * Fired after a registered DragDrop object gets the mousedown event.
16720          * Sets up the events required to track the object being dragged
16721          * @method handleMouseDown
16722          * @param {Event} e the event
16723          * @param oDD the DragDrop object being dragged
16724          * @private
16725          * @static
16726          */
16727         handleMouseDown: function(e, oDD) {
16728             if(Roo.QuickTips){
16729                 Roo.QuickTips.disable();
16730             }
16731             this.currentTarget = e.getTarget();
16732
16733             this.dragCurrent = oDD;
16734
16735             var el = oDD.getEl();
16736
16737             // track start position
16738             this.startX = e.getPageX();
16739             this.startY = e.getPageY();
16740
16741             this.deltaX = this.startX - el.offsetLeft;
16742             this.deltaY = this.startY - el.offsetTop;
16743
16744             this.dragThreshMet = false;
16745
16746             this.clickTimeout = setTimeout(
16747                     function() {
16748                         var DDM = Roo.dd.DDM;
16749                         DDM.startDrag(DDM.startX, DDM.startY);
16750                     },
16751                     this.clickTimeThresh );
16752         },
16753
16754         /**
16755          * Fired when either the drag pixel threshol or the mousedown hold
16756          * time threshold has been met.
16757          * @method startDrag
16758          * @param x {int} the X position of the original mousedown
16759          * @param y {int} the Y position of the original mousedown
16760          * @static
16761          */
16762         startDrag: function(x, y) {
16763             clearTimeout(this.clickTimeout);
16764             if (this.dragCurrent) {
16765                 this.dragCurrent.b4StartDrag(x, y);
16766                 this.dragCurrent.startDrag(x, y);
16767             }
16768             this.dragThreshMet = true;
16769         },
16770
16771         /**
16772          * Internal function to handle the mouseup event.  Will be invoked
16773          * from the context of the document.
16774          * @method handleMouseUp
16775          * @param {Event} e the event
16776          * @private
16777          * @static
16778          */
16779         handleMouseUp: function(e) {
16780
16781             if(Roo.QuickTips){
16782                 Roo.QuickTips.enable();
16783             }
16784             if (! this.dragCurrent) {
16785                 return;
16786             }
16787
16788             clearTimeout(this.clickTimeout);
16789
16790             if (this.dragThreshMet) {
16791                 this.fireEvents(e, true);
16792             } else {
16793             }
16794
16795             this.stopDrag(e);
16796
16797             this.stopEvent(e);
16798         },
16799
16800         /**
16801          * Utility to stop event propagation and event default, if these
16802          * features are turned on.
16803          * @method stopEvent
16804          * @param {Event} e the event as returned by this.getEvent()
16805          * @static
16806          */
16807         stopEvent: function(e){
16808             if(this.stopPropagation) {
16809                 e.stopPropagation();
16810             }
16811
16812             if (this.preventDefault) {
16813                 e.preventDefault();
16814             }
16815         },
16816
16817         /**
16818          * Internal function to clean up event handlers after the drag
16819          * operation is complete
16820          * @method stopDrag
16821          * @param {Event} e the event
16822          * @private
16823          * @static
16824          */
16825         stopDrag: function(e) {
16826             // Fire the drag end event for the item that was dragged
16827             if (this.dragCurrent) {
16828                 if (this.dragThreshMet) {
16829                     this.dragCurrent.b4EndDrag(e);
16830                     this.dragCurrent.endDrag(e);
16831                 }
16832
16833                 this.dragCurrent.onMouseUp(e);
16834             }
16835
16836             this.dragCurrent = null;
16837             this.dragOvers = {};
16838         },
16839
16840         /**
16841          * Internal function to handle the mousemove event.  Will be invoked
16842          * from the context of the html element.
16843          *
16844          * @TODO figure out what we can do about mouse events lost when the
16845          * user drags objects beyond the window boundary.  Currently we can
16846          * detect this in internet explorer by verifying that the mouse is
16847          * down during the mousemove event.  Firefox doesn't give us the
16848          * button state on the mousemove event.
16849          * @method handleMouseMove
16850          * @param {Event} e the event
16851          * @private
16852          * @static
16853          */
16854         handleMouseMove: function(e) {
16855             if (! this.dragCurrent) {
16856                 return true;
16857             }
16858
16859             // var button = e.which || e.button;
16860
16861             // check for IE mouseup outside of page boundary
16862             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16863                 this.stopEvent(e);
16864                 return this.handleMouseUp(e);
16865             }
16866
16867             if (!this.dragThreshMet) {
16868                 var diffX = Math.abs(this.startX - e.getPageX());
16869                 var diffY = Math.abs(this.startY - e.getPageY());
16870                 if (diffX > this.clickPixelThresh ||
16871                             diffY > this.clickPixelThresh) {
16872                     this.startDrag(this.startX, this.startY);
16873                 }
16874             }
16875
16876             if (this.dragThreshMet) {
16877                 this.dragCurrent.b4Drag(e);
16878                 this.dragCurrent.onDrag(e);
16879                 if(!this.dragCurrent.moveOnly){
16880                     this.fireEvents(e, false);
16881                 }
16882             }
16883
16884             this.stopEvent(e);
16885
16886             return true;
16887         },
16888
16889         /**
16890          * Iterates over all of the DragDrop elements to find ones we are
16891          * hovering over or dropping on
16892          * @method fireEvents
16893          * @param {Event} e the event
16894          * @param {boolean} isDrop is this a drop op or a mouseover op?
16895          * @private
16896          * @static
16897          */
16898         fireEvents: function(e, isDrop) {
16899             var dc = this.dragCurrent;
16900
16901             // If the user did the mouse up outside of the window, we could
16902             // get here even though we have ended the drag.
16903             if (!dc || dc.isLocked()) {
16904                 return;
16905             }
16906
16907             var pt = e.getPoint();
16908
16909             // cache the previous dragOver array
16910             var oldOvers = [];
16911
16912             var outEvts   = [];
16913             var overEvts  = [];
16914             var dropEvts  = [];
16915             var enterEvts = [];
16916
16917             // Check to see if the object(s) we were hovering over is no longer
16918             // being hovered over so we can fire the onDragOut event
16919             for (var i in this.dragOvers) {
16920
16921                 var ddo = this.dragOvers[i];
16922
16923                 if (! this.isTypeOfDD(ddo)) {
16924                     continue;
16925                 }
16926
16927                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16928                     outEvts.push( ddo );
16929                 }
16930
16931                 oldOvers[i] = true;
16932                 delete this.dragOvers[i];
16933             }
16934
16935             for (var sGroup in dc.groups) {
16936
16937                 if ("string" != typeof sGroup) {
16938                     continue;
16939                 }
16940
16941                 for (i in this.ids[sGroup]) {
16942                     var oDD = this.ids[sGroup][i];
16943                     if (! this.isTypeOfDD(oDD)) {
16944                         continue;
16945                     }
16946
16947                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16948                         if (this.isOverTarget(pt, oDD, this.mode)) {
16949                             // look for drop interactions
16950                             if (isDrop) {
16951                                 dropEvts.push( oDD );
16952                             // look for drag enter and drag over interactions
16953                             } else {
16954
16955                                 // initial drag over: dragEnter fires
16956                                 if (!oldOvers[oDD.id]) {
16957                                     enterEvts.push( oDD );
16958                                 // subsequent drag overs: dragOver fires
16959                                 } else {
16960                                     overEvts.push( oDD );
16961                                 }
16962
16963                                 this.dragOvers[oDD.id] = oDD;
16964                             }
16965                         }
16966                     }
16967                 }
16968             }
16969
16970             if (this.mode) {
16971                 if (outEvts.length) {
16972                     dc.b4DragOut(e, outEvts);
16973                     dc.onDragOut(e, outEvts);
16974                 }
16975
16976                 if (enterEvts.length) {
16977                     dc.onDragEnter(e, enterEvts);
16978                 }
16979
16980                 if (overEvts.length) {
16981                     dc.b4DragOver(e, overEvts);
16982                     dc.onDragOver(e, overEvts);
16983                 }
16984
16985                 if (dropEvts.length) {
16986                     dc.b4DragDrop(e, dropEvts);
16987                     dc.onDragDrop(e, dropEvts);
16988                 }
16989
16990             } else {
16991                 // fire dragout events
16992                 var len = 0;
16993                 for (i=0, len=outEvts.length; i<len; ++i) {
16994                     dc.b4DragOut(e, outEvts[i].id);
16995                     dc.onDragOut(e, outEvts[i].id);
16996                 }
16997
16998                 // fire enter events
16999                 for (i=0,len=enterEvts.length; i<len; ++i) {
17000                     // dc.b4DragEnter(e, oDD.id);
17001                     dc.onDragEnter(e, enterEvts[i].id);
17002                 }
17003
17004                 // fire over events
17005                 for (i=0,len=overEvts.length; i<len; ++i) {
17006                     dc.b4DragOver(e, overEvts[i].id);
17007                     dc.onDragOver(e, overEvts[i].id);
17008                 }
17009
17010                 // fire drop events
17011                 for (i=0, len=dropEvts.length; i<len; ++i) {
17012                     dc.b4DragDrop(e, dropEvts[i].id);
17013                     dc.onDragDrop(e, dropEvts[i].id);
17014                 }
17015
17016             }
17017
17018             // notify about a drop that did not find a target
17019             if (isDrop && !dropEvts.length) {
17020                 dc.onInvalidDrop(e);
17021             }
17022
17023         },
17024
17025         /**
17026          * Helper function for getting the best match from the list of drag
17027          * and drop objects returned by the drag and drop events when we are
17028          * in INTERSECT mode.  It returns either the first object that the
17029          * cursor is over, or the object that has the greatest overlap with
17030          * the dragged element.
17031          * @method getBestMatch
17032          * @param  {DragDrop[]} dds The array of drag and drop objects
17033          * targeted
17034          * @return {DragDrop}       The best single match
17035          * @static
17036          */
17037         getBestMatch: function(dds) {
17038             var winner = null;
17039             // Return null if the input is not what we expect
17040             //if (!dds || !dds.length || dds.length == 0) {
17041                // winner = null;
17042             // If there is only one item, it wins
17043             //} else if (dds.length == 1) {
17044
17045             var len = dds.length;
17046
17047             if (len == 1) {
17048                 winner = dds[0];
17049             } else {
17050                 // Loop through the targeted items
17051                 for (var i=0; i<len; ++i) {
17052                     var dd = dds[i];
17053                     // If the cursor is over the object, it wins.  If the
17054                     // cursor is over multiple matches, the first one we come
17055                     // to wins.
17056                     if (dd.cursorIsOver) {
17057                         winner = dd;
17058                         break;
17059                     // Otherwise the object with the most overlap wins
17060                     } else {
17061                         if (!winner ||
17062                             winner.overlap.getArea() < dd.overlap.getArea()) {
17063                             winner = dd;
17064                         }
17065                     }
17066                 }
17067             }
17068
17069             return winner;
17070         },
17071
17072         /**
17073          * Refreshes the cache of the top-left and bottom-right points of the
17074          * drag and drop objects in the specified group(s).  This is in the
17075          * format that is stored in the drag and drop instance, so typical
17076          * usage is:
17077          * <code>
17078          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
17079          * </code>
17080          * Alternatively:
17081          * <code>
17082          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
17083          * </code>
17084          * @TODO this really should be an indexed array.  Alternatively this
17085          * method could accept both.
17086          * @method refreshCache
17087          * @param {Object} groups an associative array of groups to refresh
17088          * @static
17089          */
17090         refreshCache: function(groups) {
17091             for (var sGroup in groups) {
17092                 if ("string" != typeof sGroup) {
17093                     continue;
17094                 }
17095                 for (var i in this.ids[sGroup]) {
17096                     var oDD = this.ids[sGroup][i];
17097
17098                     if (this.isTypeOfDD(oDD)) {
17099                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
17100                         var loc = this.getLocation(oDD);
17101                         if (loc) {
17102                             this.locationCache[oDD.id] = loc;
17103                         } else {
17104                             delete this.locationCache[oDD.id];
17105                             // this will unregister the drag and drop object if
17106                             // the element is not in a usable state
17107                             // oDD.unreg();
17108                         }
17109                     }
17110                 }
17111             }
17112         },
17113
17114         /**
17115          * This checks to make sure an element exists and is in the DOM.  The
17116          * main purpose is to handle cases where innerHTML is used to remove
17117          * drag and drop objects from the DOM.  IE provides an 'unspecified
17118          * error' when trying to access the offsetParent of such an element
17119          * @method verifyEl
17120          * @param {HTMLElement} el the element to check
17121          * @return {boolean} true if the element looks usable
17122          * @static
17123          */
17124         verifyEl: function(el) {
17125             if (el) {
17126                 var parent;
17127                 if(Roo.isIE){
17128                     try{
17129                         parent = el.offsetParent;
17130                     }catch(e){}
17131                 }else{
17132                     parent = el.offsetParent;
17133                 }
17134                 if (parent) {
17135                     return true;
17136                 }
17137             }
17138
17139             return false;
17140         },
17141
17142         /**
17143          * Returns a Region object containing the drag and drop element's position
17144          * and size, including the padding configured for it
17145          * @method getLocation
17146          * @param {DragDrop} oDD the drag and drop object to get the
17147          *                       location for
17148          * @return {Roo.lib.Region} a Region object representing the total area
17149          *                             the element occupies, including any padding
17150          *                             the instance is configured for.
17151          * @static
17152          */
17153         getLocation: function(oDD) {
17154             if (! this.isTypeOfDD(oDD)) {
17155                 return null;
17156             }
17157
17158             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
17159
17160             try {
17161                 pos= Roo.lib.Dom.getXY(el);
17162             } catch (e) { }
17163
17164             if (!pos) {
17165                 return null;
17166             }
17167
17168             x1 = pos[0];
17169             x2 = x1 + el.offsetWidth;
17170             y1 = pos[1];
17171             y2 = y1 + el.offsetHeight;
17172
17173             t = y1 - oDD.padding[0];
17174             r = x2 + oDD.padding[1];
17175             b = y2 + oDD.padding[2];
17176             l = x1 - oDD.padding[3];
17177
17178             return new Roo.lib.Region( t, r, b, l );
17179         },
17180
17181         /**
17182          * Checks the cursor location to see if it over the target
17183          * @method isOverTarget
17184          * @param {Roo.lib.Point} pt The point to evaluate
17185          * @param {DragDrop} oTarget the DragDrop object we are inspecting
17186          * @return {boolean} true if the mouse is over the target
17187          * @private
17188          * @static
17189          */
17190         isOverTarget: function(pt, oTarget, intersect) {
17191             // use cache if available
17192             var loc = this.locationCache[oTarget.id];
17193             if (!loc || !this.useCache) {
17194                 loc = this.getLocation(oTarget);
17195                 this.locationCache[oTarget.id] = loc;
17196
17197             }
17198
17199             if (!loc) {
17200                 return false;
17201             }
17202
17203             oTarget.cursorIsOver = loc.contains( pt );
17204
17205             // DragDrop is using this as a sanity check for the initial mousedown
17206             // in this case we are done.  In POINT mode, if the drag obj has no
17207             // contraints, we are also done. Otherwise we need to evaluate the
17208             // location of the target as related to the actual location of the
17209             // dragged element.
17210             var dc = this.dragCurrent;
17211             if (!dc || !dc.getTargetCoord ||
17212                     (!intersect && !dc.constrainX && !dc.constrainY)) {
17213                 return oTarget.cursorIsOver;
17214             }
17215
17216             oTarget.overlap = null;
17217
17218             // Get the current location of the drag element, this is the
17219             // location of the mouse event less the delta that represents
17220             // where the original mousedown happened on the element.  We
17221             // need to consider constraints and ticks as well.
17222             var pos = dc.getTargetCoord(pt.x, pt.y);
17223
17224             var el = dc.getDragEl();
17225             var curRegion = new Roo.lib.Region( pos.y,
17226                                                    pos.x + el.offsetWidth,
17227                                                    pos.y + el.offsetHeight,
17228                                                    pos.x );
17229
17230             var overlap = curRegion.intersect(loc);
17231
17232             if (overlap) {
17233                 oTarget.overlap = overlap;
17234                 return (intersect) ? true : oTarget.cursorIsOver;
17235             } else {
17236                 return false;
17237             }
17238         },
17239
17240         /**
17241          * unload event handler
17242          * @method _onUnload
17243          * @private
17244          * @static
17245          */
17246         _onUnload: function(e, me) {
17247             Roo.dd.DragDropMgr.unregAll();
17248         },
17249
17250         /**
17251          * Cleans up the drag and drop events and objects.
17252          * @method unregAll
17253          * @private
17254          * @static
17255          */
17256         unregAll: function() {
17257
17258             if (this.dragCurrent) {
17259                 this.stopDrag();
17260                 this.dragCurrent = null;
17261             }
17262
17263             this._execOnAll("unreg", []);
17264
17265             for (i in this.elementCache) {
17266                 delete this.elementCache[i];
17267             }
17268
17269             this.elementCache = {};
17270             this.ids = {};
17271         },
17272
17273         /**
17274          * A cache of DOM elements
17275          * @property elementCache
17276          * @private
17277          * @static
17278          */
17279         elementCache: {},
17280
17281         /**
17282          * Get the wrapper for the DOM element specified
17283          * @method getElWrapper
17284          * @param {String} id the id of the element to get
17285          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
17286          * @private
17287          * @deprecated This wrapper isn't that useful
17288          * @static
17289          */
17290         getElWrapper: function(id) {
17291             var oWrapper = this.elementCache[id];
17292             if (!oWrapper || !oWrapper.el) {
17293                 oWrapper = this.elementCache[id] =
17294                     new this.ElementWrapper(Roo.getDom(id));
17295             }
17296             return oWrapper;
17297         },
17298
17299         /**
17300          * Returns the actual DOM element
17301          * @method getElement
17302          * @param {String} id the id of the elment to get
17303          * @return {Object} The element
17304          * @deprecated use Roo.getDom instead
17305          * @static
17306          */
17307         getElement: function(id) {
17308             return Roo.getDom(id);
17309         },
17310
17311         /**
17312          * Returns the style property for the DOM element (i.e.,
17313          * document.getElById(id).style)
17314          * @method getCss
17315          * @param {String} id the id of the elment to get
17316          * @return {Object} The style property of the element
17317          * @deprecated use Roo.getDom instead
17318          * @static
17319          */
17320         getCss: function(id) {
17321             var el = Roo.getDom(id);
17322             return (el) ? el.style : null;
17323         },
17324
17325         /**
17326          * Inner class for cached elements
17327          * @class DragDropMgr.ElementWrapper
17328          * @for DragDropMgr
17329          * @private
17330          * @deprecated
17331          */
17332         ElementWrapper: function(el) {
17333                 /**
17334                  * The element
17335                  * @property el
17336                  */
17337                 this.el = el || null;
17338                 /**
17339                  * The element id
17340                  * @property id
17341                  */
17342                 this.id = this.el && el.id;
17343                 /**
17344                  * A reference to the style property
17345                  * @property css
17346                  */
17347                 this.css = this.el && el.style;
17348             },
17349
17350         /**
17351          * Returns the X position of an html element
17352          * @method getPosX
17353          * @param el the element for which to get the position
17354          * @return {int} the X coordinate
17355          * @for DragDropMgr
17356          * @deprecated use Roo.lib.Dom.getX instead
17357          * @static
17358          */
17359         getPosX: function(el) {
17360             return Roo.lib.Dom.getX(el);
17361         },
17362
17363         /**
17364          * Returns the Y position of an html element
17365          * @method getPosY
17366          * @param el the element for which to get the position
17367          * @return {int} the Y coordinate
17368          * @deprecated use Roo.lib.Dom.getY instead
17369          * @static
17370          */
17371         getPosY: function(el) {
17372             return Roo.lib.Dom.getY(el);
17373         },
17374
17375         /**
17376          * Swap two nodes.  In IE, we use the native method, for others we
17377          * emulate the IE behavior
17378          * @method swapNode
17379          * @param n1 the first node to swap
17380          * @param n2 the other node to swap
17381          * @static
17382          */
17383         swapNode: function(n1, n2) {
17384             if (n1.swapNode) {
17385                 n1.swapNode(n2);
17386             } else {
17387                 var p = n2.parentNode;
17388                 var s = n2.nextSibling;
17389
17390                 if (s == n1) {
17391                     p.insertBefore(n1, n2);
17392                 } else if (n2 == n1.nextSibling) {
17393                     p.insertBefore(n2, n1);
17394                 } else {
17395                     n1.parentNode.replaceChild(n2, n1);
17396                     p.insertBefore(n1, s);
17397                 }
17398             }
17399         },
17400
17401         /**
17402          * Returns the current scroll position
17403          * @method getScroll
17404          * @private
17405          * @static
17406          */
17407         getScroll: function () {
17408             var t, l, dde=document.documentElement, db=document.body;
17409             if (dde && (dde.scrollTop || dde.scrollLeft)) {
17410                 t = dde.scrollTop;
17411                 l = dde.scrollLeft;
17412             } else if (db) {
17413                 t = db.scrollTop;
17414                 l = db.scrollLeft;
17415             } else {
17416
17417             }
17418             return { top: t, left: l };
17419         },
17420
17421         /**
17422          * Returns the specified element style property
17423          * @method getStyle
17424          * @param {HTMLElement} el          the element
17425          * @param {string}      styleProp   the style property
17426          * @return {string} The value of the style property
17427          * @deprecated use Roo.lib.Dom.getStyle
17428          * @static
17429          */
17430         getStyle: function(el, styleProp) {
17431             return Roo.fly(el).getStyle(styleProp);
17432         },
17433
17434         /**
17435          * Gets the scrollTop
17436          * @method getScrollTop
17437          * @return {int} the document's scrollTop
17438          * @static
17439          */
17440         getScrollTop: function () { return this.getScroll().top; },
17441
17442         /**
17443          * Gets the scrollLeft
17444          * @method getScrollLeft
17445          * @return {int} the document's scrollTop
17446          * @static
17447          */
17448         getScrollLeft: function () { return this.getScroll().left; },
17449
17450         /**
17451          * Sets the x/y position of an element to the location of the
17452          * target element.
17453          * @method moveToEl
17454          * @param {HTMLElement} moveEl      The element to move
17455          * @param {HTMLElement} targetEl    The position reference element
17456          * @static
17457          */
17458         moveToEl: function (moveEl, targetEl) {
17459             var aCoord = Roo.lib.Dom.getXY(targetEl);
17460             Roo.lib.Dom.setXY(moveEl, aCoord);
17461         },
17462
17463         /**
17464          * Numeric array sort function
17465          * @method numericSort
17466          * @static
17467          */
17468         numericSort: function(a, b) { return (a - b); },
17469
17470         /**
17471          * Internal counter
17472          * @property _timeoutCount
17473          * @private
17474          * @static
17475          */
17476         _timeoutCount: 0,
17477
17478         /**
17479          * Trying to make the load order less important.  Without this we get
17480          * an error if this file is loaded before the Event Utility.
17481          * @method _addListeners
17482          * @private
17483          * @static
17484          */
17485         _addListeners: function() {
17486             var DDM = Roo.dd.DDM;
17487             if ( Roo.lib.Event && document ) {
17488                 DDM._onLoad();
17489             } else {
17490                 if (DDM._timeoutCount > 2000) {
17491                 } else {
17492                     setTimeout(DDM._addListeners, 10);
17493                     if (document && document.body) {
17494                         DDM._timeoutCount += 1;
17495                     }
17496                 }
17497             }
17498         },
17499
17500         /**
17501          * Recursively searches the immediate parent and all child nodes for
17502          * the handle element in order to determine wheter or not it was
17503          * clicked.
17504          * @method handleWasClicked
17505          * @param node the html element to inspect
17506          * @static
17507          */
17508         handleWasClicked: function(node, id) {
17509             if (this.isHandle(id, node.id)) {
17510                 return true;
17511             } else {
17512                 // check to see if this is a text node child of the one we want
17513                 var p = node.parentNode;
17514
17515                 while (p) {
17516                     if (this.isHandle(id, p.id)) {
17517                         return true;
17518                     } else {
17519                         p = p.parentNode;
17520                     }
17521                 }
17522             }
17523
17524             return false;
17525         }
17526
17527     };
17528
17529 }();
17530
17531 // shorter alias, save a few bytes
17532 Roo.dd.DDM = Roo.dd.DragDropMgr;
17533 Roo.dd.DDM._addListeners();
17534
17535 }/*
17536  * Based on:
17537  * Ext JS Library 1.1.1
17538  * Copyright(c) 2006-2007, Ext JS, LLC.
17539  *
17540  * Originally Released Under LGPL - original licence link has changed is not relivant.
17541  *
17542  * Fork - LGPL
17543  * <script type="text/javascript">
17544  */
17545
17546 /**
17547  * @class Roo.dd.DD
17548  * A DragDrop implementation where the linked element follows the
17549  * mouse cursor during a drag.
17550  * @extends Roo.dd.DragDrop
17551  * @constructor
17552  * @param {String} id the id of the linked element
17553  * @param {String} sGroup the group of related DragDrop items
17554  * @param {object} config an object containing configurable attributes
17555  *                Valid properties for DD:
17556  *                    scroll
17557  */
17558 Roo.dd.DD = function(id, sGroup, config) {
17559     if (id) {
17560         this.init(id, sGroup, config);
17561     }
17562 };
17563
17564 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
17565
17566     /**
17567      * When set to true, the utility automatically tries to scroll the browser
17568      * window wehn a drag and drop element is dragged near the viewport boundary.
17569      * Defaults to true.
17570      * @property scroll
17571      * @type boolean
17572      */
17573     scroll: true,
17574
17575     /**
17576      * Sets the pointer offset to the distance between the linked element's top
17577      * left corner and the location the element was clicked
17578      * @method autoOffset
17579      * @param {int} iPageX the X coordinate of the click
17580      * @param {int} iPageY the Y coordinate of the click
17581      */
17582     autoOffset: function(iPageX, iPageY) {
17583         var x = iPageX - this.startPageX;
17584         var y = iPageY - this.startPageY;
17585         this.setDelta(x, y);
17586     },
17587
17588     /**
17589      * Sets the pointer offset.  You can call this directly to force the
17590      * offset to be in a particular location (e.g., pass in 0,0 to set it
17591      * to the center of the object)
17592      * @method setDelta
17593      * @param {int} iDeltaX the distance from the left
17594      * @param {int} iDeltaY the distance from the top
17595      */
17596     setDelta: function(iDeltaX, iDeltaY) {
17597         this.deltaX = iDeltaX;
17598         this.deltaY = iDeltaY;
17599     },
17600
17601     /**
17602      * Sets the drag element to the location of the mousedown or click event,
17603      * maintaining the cursor location relative to the location on the element
17604      * that was clicked.  Override this if you want to place the element in a
17605      * location other than where the cursor is.
17606      * @method setDragElPos
17607      * @param {int} iPageX the X coordinate of the mousedown or drag event
17608      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17609      */
17610     setDragElPos: function(iPageX, iPageY) {
17611         // the first time we do this, we are going to check to make sure
17612         // the element has css positioning
17613
17614         var el = this.getDragEl();
17615         this.alignElWithMouse(el, iPageX, iPageY);
17616     },
17617
17618     /**
17619      * Sets the element to the location of the mousedown or click event,
17620      * maintaining the cursor location relative to the location on the element
17621      * that was clicked.  Override this if you want to place the element in a
17622      * location other than where the cursor is.
17623      * @method alignElWithMouse
17624      * @param {HTMLElement} el the element to move
17625      * @param {int} iPageX the X coordinate of the mousedown or drag event
17626      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17627      */
17628     alignElWithMouse: function(el, iPageX, iPageY) {
17629         var oCoord = this.getTargetCoord(iPageX, iPageY);
17630         var fly = el.dom ? el : Roo.fly(el);
17631         if (!this.deltaSetXY) {
17632             var aCoord = [oCoord.x, oCoord.y];
17633             fly.setXY(aCoord);
17634             var newLeft = fly.getLeft(true);
17635             var newTop  = fly.getTop(true);
17636             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17637         } else {
17638             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17639         }
17640
17641         this.cachePosition(oCoord.x, oCoord.y);
17642         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17643         return oCoord;
17644     },
17645
17646     /**
17647      * Saves the most recent position so that we can reset the constraints and
17648      * tick marks on-demand.  We need to know this so that we can calculate the
17649      * number of pixels the element is offset from its original position.
17650      * @method cachePosition
17651      * @param iPageX the current x position (optional, this just makes it so we
17652      * don't have to look it up again)
17653      * @param iPageY the current y position (optional, this just makes it so we
17654      * don't have to look it up again)
17655      */
17656     cachePosition: function(iPageX, iPageY) {
17657         if (iPageX) {
17658             this.lastPageX = iPageX;
17659             this.lastPageY = iPageY;
17660         } else {
17661             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17662             this.lastPageX = aCoord[0];
17663             this.lastPageY = aCoord[1];
17664         }
17665     },
17666
17667     /**
17668      * Auto-scroll the window if the dragged object has been moved beyond the
17669      * visible window boundary.
17670      * @method autoScroll
17671      * @param {int} x the drag element's x position
17672      * @param {int} y the drag element's y position
17673      * @param {int} h the height of the drag element
17674      * @param {int} w the width of the drag element
17675      * @private
17676      */
17677     autoScroll: function(x, y, h, w) {
17678
17679         if (this.scroll) {
17680             // The client height
17681             var clientH = Roo.lib.Dom.getViewWidth();
17682
17683             // The client width
17684             var clientW = Roo.lib.Dom.getViewHeight();
17685
17686             // The amt scrolled down
17687             var st = this.DDM.getScrollTop();
17688
17689             // The amt scrolled right
17690             var sl = this.DDM.getScrollLeft();
17691
17692             // Location of the bottom of the element
17693             var bot = h + y;
17694
17695             // Location of the right of the element
17696             var right = w + x;
17697
17698             // The distance from the cursor to the bottom of the visible area,
17699             // adjusted so that we don't scroll if the cursor is beyond the
17700             // element drag constraints
17701             var toBot = (clientH + st - y - this.deltaY);
17702
17703             // The distance from the cursor to the right of the visible area
17704             var toRight = (clientW + sl - x - this.deltaX);
17705
17706
17707             // How close to the edge the cursor must be before we scroll
17708             // var thresh = (document.all) ? 100 : 40;
17709             var thresh = 40;
17710
17711             // How many pixels to scroll per autoscroll op.  This helps to reduce
17712             // clunky scrolling. IE is more sensitive about this ... it needs this
17713             // value to be higher.
17714             var scrAmt = (document.all) ? 80 : 30;
17715
17716             // Scroll down if we are near the bottom of the visible page and the
17717             // obj extends below the crease
17718             if ( bot > clientH && toBot < thresh ) {
17719                 window.scrollTo(sl, st + scrAmt);
17720             }
17721
17722             // Scroll up if the window is scrolled down and the top of the object
17723             // goes above the top border
17724             if ( y < st && st > 0 && y - st < thresh ) {
17725                 window.scrollTo(sl, st - scrAmt);
17726             }
17727
17728             // Scroll right if the obj is beyond the right border and the cursor is
17729             // near the border.
17730             if ( right > clientW && toRight < thresh ) {
17731                 window.scrollTo(sl + scrAmt, st);
17732             }
17733
17734             // Scroll left if the window has been scrolled to the right and the obj
17735             // extends past the left border
17736             if ( x < sl && sl > 0 && x - sl < thresh ) {
17737                 window.scrollTo(sl - scrAmt, st);
17738             }
17739         }
17740     },
17741
17742     /**
17743      * Finds the location the element should be placed if we want to move
17744      * it to where the mouse location less the click offset would place us.
17745      * @method getTargetCoord
17746      * @param {int} iPageX the X coordinate of the click
17747      * @param {int} iPageY the Y coordinate of the click
17748      * @return an object that contains the coordinates (Object.x and Object.y)
17749      * @private
17750      */
17751     getTargetCoord: function(iPageX, iPageY) {
17752
17753
17754         var x = iPageX - this.deltaX;
17755         var y = iPageY - this.deltaY;
17756
17757         if (this.constrainX) {
17758             if (x < this.minX) { x = this.minX; }
17759             if (x > this.maxX) { x = this.maxX; }
17760         }
17761
17762         if (this.constrainY) {
17763             if (y < this.minY) { y = this.minY; }
17764             if (y > this.maxY) { y = this.maxY; }
17765         }
17766
17767         x = this.getTick(x, this.xTicks);
17768         y = this.getTick(y, this.yTicks);
17769
17770
17771         return {x:x, y:y};
17772     },
17773
17774     /*
17775      * Sets up config options specific to this class. Overrides
17776      * Roo.dd.DragDrop, but all versions of this method through the
17777      * inheritance chain are called
17778      */
17779     applyConfig: function() {
17780         Roo.dd.DD.superclass.applyConfig.call(this);
17781         this.scroll = (this.config.scroll !== false);
17782     },
17783
17784     /*
17785      * Event that fires prior to the onMouseDown event.  Overrides
17786      * Roo.dd.DragDrop.
17787      */
17788     b4MouseDown: function(e) {
17789         // this.resetConstraints();
17790         this.autoOffset(e.getPageX(),
17791                             e.getPageY());
17792     },
17793
17794     /*
17795      * Event that fires prior to the onDrag event.  Overrides
17796      * Roo.dd.DragDrop.
17797      */
17798     b4Drag: function(e) {
17799         this.setDragElPos(e.getPageX(),
17800                             e.getPageY());
17801     },
17802
17803     toString: function() {
17804         return ("DD " + this.id);
17805     }
17806
17807     //////////////////////////////////////////////////////////////////////////
17808     // Debugging ygDragDrop events that can be overridden
17809     //////////////////////////////////////////////////////////////////////////
17810     /*
17811     startDrag: function(x, y) {
17812     },
17813
17814     onDrag: function(e) {
17815     },
17816
17817     onDragEnter: function(e, id) {
17818     },
17819
17820     onDragOver: function(e, id) {
17821     },
17822
17823     onDragOut: function(e, id) {
17824     },
17825
17826     onDragDrop: function(e, id) {
17827     },
17828
17829     endDrag: function(e) {
17830     }
17831
17832     */
17833
17834 });/*
17835  * Based on:
17836  * Ext JS Library 1.1.1
17837  * Copyright(c) 2006-2007, Ext JS, LLC.
17838  *
17839  * Originally Released Under LGPL - original licence link has changed is not relivant.
17840  *
17841  * Fork - LGPL
17842  * <script type="text/javascript">
17843  */
17844
17845 /**
17846  * @class Roo.dd.DDProxy
17847  * A DragDrop implementation that inserts an empty, bordered div into
17848  * the document that follows the cursor during drag operations.  At the time of
17849  * the click, the frame div is resized to the dimensions of the linked html
17850  * element, and moved to the exact location of the linked element.
17851  *
17852  * References to the "frame" element refer to the single proxy element that
17853  * was created to be dragged in place of all DDProxy elements on the
17854  * page.
17855  *
17856  * @extends Roo.dd.DD
17857  * @constructor
17858  * @param {String} id the id of the linked html element
17859  * @param {String} sGroup the group of related DragDrop objects
17860  * @param {object} config an object containing configurable attributes
17861  *                Valid properties for DDProxy in addition to those in DragDrop:
17862  *                   resizeFrame, centerFrame, dragElId
17863  */
17864 Roo.dd.DDProxy = function(id, sGroup, config) {
17865     if (id) {
17866         this.init(id, sGroup, config);
17867         this.initFrame();
17868     }
17869 };
17870
17871 /**
17872  * The default drag frame div id
17873  * @property Roo.dd.DDProxy.dragElId
17874  * @type String
17875  * @static
17876  */
17877 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17878
17879 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17880
17881     /**
17882      * By default we resize the drag frame to be the same size as the element
17883      * we want to drag (this is to get the frame effect).  We can turn it off
17884      * if we want a different behavior.
17885      * @property resizeFrame
17886      * @type boolean
17887      */
17888     resizeFrame: true,
17889
17890     /**
17891      * By default the frame is positioned exactly where the drag element is, so
17892      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17893      * you do not have constraints on the obj is to have the drag frame centered
17894      * around the cursor.  Set centerFrame to true for this effect.
17895      * @property centerFrame
17896      * @type boolean
17897      */
17898     centerFrame: false,
17899
17900     /**
17901      * Creates the proxy element if it does not yet exist
17902      * @method createFrame
17903      */
17904     createFrame: function() {
17905         var self = this;
17906         var body = document.body;
17907
17908         if (!body || !body.firstChild) {
17909             setTimeout( function() { self.createFrame(); }, 50 );
17910             return;
17911         }
17912
17913         var div = this.getDragEl();
17914
17915         if (!div) {
17916             div    = document.createElement("div");
17917             div.id = this.dragElId;
17918             var s  = div.style;
17919
17920             s.position   = "absolute";
17921             s.visibility = "hidden";
17922             s.cursor     = "move";
17923             s.border     = "2px solid #aaa";
17924             s.zIndex     = 999;
17925
17926             // appendChild can blow up IE if invoked prior to the window load event
17927             // while rendering a table.  It is possible there are other scenarios
17928             // that would cause this to happen as well.
17929             body.insertBefore(div, body.firstChild);
17930         }
17931     },
17932
17933     /**
17934      * Initialization for the drag frame element.  Must be called in the
17935      * constructor of all subclasses
17936      * @method initFrame
17937      */
17938     initFrame: function() {
17939         this.createFrame();
17940     },
17941
17942     applyConfig: function() {
17943         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17944
17945         this.resizeFrame = (this.config.resizeFrame !== false);
17946         this.centerFrame = (this.config.centerFrame);
17947         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17948     },
17949
17950     /**
17951      * Resizes the drag frame to the dimensions of the clicked object, positions
17952      * it over the object, and finally displays it
17953      * @method showFrame
17954      * @param {int} iPageX X click position
17955      * @param {int} iPageY Y click position
17956      * @private
17957      */
17958     showFrame: function(iPageX, iPageY) {
17959         var el = this.getEl();
17960         var dragEl = this.getDragEl();
17961         var s = dragEl.style;
17962
17963         this._resizeProxy();
17964
17965         if (this.centerFrame) {
17966             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17967                            Math.round(parseInt(s.height, 10)/2) );
17968         }
17969
17970         this.setDragElPos(iPageX, iPageY);
17971
17972         Roo.fly(dragEl).show();
17973     },
17974
17975     /**
17976      * The proxy is automatically resized to the dimensions of the linked
17977      * element when a drag is initiated, unless resizeFrame is set to false
17978      * @method _resizeProxy
17979      * @private
17980      */
17981     _resizeProxy: function() {
17982         if (this.resizeFrame) {
17983             var el = this.getEl();
17984             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17985         }
17986     },
17987
17988     // overrides Roo.dd.DragDrop
17989     b4MouseDown: function(e) {
17990         var x = e.getPageX();
17991         var y = e.getPageY();
17992         this.autoOffset(x, y);
17993         this.setDragElPos(x, y);
17994     },
17995
17996     // overrides Roo.dd.DragDrop
17997     b4StartDrag: function(x, y) {
17998         // show the drag frame
17999         this.showFrame(x, y);
18000     },
18001
18002     // overrides Roo.dd.DragDrop
18003     b4EndDrag: function(e) {
18004         Roo.fly(this.getDragEl()).hide();
18005     },
18006
18007     // overrides Roo.dd.DragDrop
18008     // By default we try to move the element to the last location of the frame.
18009     // This is so that the default behavior mirrors that of Roo.dd.DD.
18010     endDrag: function(e) {
18011
18012         var lel = this.getEl();
18013         var del = this.getDragEl();
18014
18015         // Show the drag frame briefly so we can get its position
18016         del.style.visibility = "";
18017
18018         this.beforeMove();
18019         // Hide the linked element before the move to get around a Safari
18020         // rendering bug.
18021         lel.style.visibility = "hidden";
18022         Roo.dd.DDM.moveToEl(lel, del);
18023         del.style.visibility = "hidden";
18024         lel.style.visibility = "";
18025
18026         this.afterDrag();
18027     },
18028
18029     beforeMove : function(){
18030
18031     },
18032
18033     afterDrag : function(){
18034
18035     },
18036
18037     toString: function() {
18038         return ("DDProxy " + this.id);
18039     }
18040
18041 });
18042 /*
18043  * Based on:
18044  * Ext JS Library 1.1.1
18045  * Copyright(c) 2006-2007, Ext JS, LLC.
18046  *
18047  * Originally Released Under LGPL - original licence link has changed is not relivant.
18048  *
18049  * Fork - LGPL
18050  * <script type="text/javascript">
18051  */
18052
18053  /**
18054  * @class Roo.dd.DDTarget
18055  * A DragDrop implementation that does not move, but can be a drop
18056  * target.  You would get the same result by simply omitting implementation
18057  * for the event callbacks, but this way we reduce the processing cost of the
18058  * event listener and the callbacks.
18059  * @extends Roo.dd.DragDrop
18060  * @constructor
18061  * @param {String} id the id of the element that is a drop target
18062  * @param {String} sGroup the group of related DragDrop objects
18063  * @param {object} config an object containing configurable attributes
18064  *                 Valid properties for DDTarget in addition to those in
18065  *                 DragDrop:
18066  *                    none
18067  */
18068 Roo.dd.DDTarget = function(id, sGroup, config) {
18069     if (id) {
18070         this.initTarget(id, sGroup, config);
18071     }
18072     if (config.listeners || config.events) { 
18073        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
18074             listeners : config.listeners || {}, 
18075             events : config.events || {} 
18076         });    
18077     }
18078 };
18079
18080 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
18081 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
18082     toString: function() {
18083         return ("DDTarget " + this.id);
18084     }
18085 });
18086 /*
18087  * Based on:
18088  * Ext JS Library 1.1.1
18089  * Copyright(c) 2006-2007, Ext JS, LLC.
18090  *
18091  * Originally Released Under LGPL - original licence link has changed is not relivant.
18092  *
18093  * Fork - LGPL
18094  * <script type="text/javascript">
18095  */
18096  
18097
18098 /**
18099  * @class Roo.dd.ScrollManager
18100  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
18101  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
18102  * @singleton
18103  */
18104 Roo.dd.ScrollManager = function(){
18105     var ddm = Roo.dd.DragDropMgr;
18106     var els = {};
18107     var dragEl = null;
18108     var proc = {};
18109     
18110     
18111     
18112     var onStop = function(e){
18113         dragEl = null;
18114         clearProc();
18115     };
18116     
18117     var triggerRefresh = function(){
18118         if(ddm.dragCurrent){
18119              ddm.refreshCache(ddm.dragCurrent.groups);
18120         }
18121     };
18122     
18123     var doScroll = function(){
18124         if(ddm.dragCurrent){
18125             var dds = Roo.dd.ScrollManager;
18126             if(!dds.animate){
18127                 if(proc.el.scroll(proc.dir, dds.increment)){
18128                     triggerRefresh();
18129                 }
18130             }else{
18131                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
18132             }
18133         }
18134     };
18135     
18136     var clearProc = function(){
18137         if(proc.id){
18138             clearInterval(proc.id);
18139         }
18140         proc.id = 0;
18141         proc.el = null;
18142         proc.dir = "";
18143     };
18144     
18145     var startProc = function(el, dir){
18146          Roo.log('scroll startproc');
18147         clearProc();
18148         proc.el = el;
18149         proc.dir = dir;
18150         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
18151     };
18152     
18153     var onFire = function(e, isDrop){
18154        
18155         if(isDrop || !ddm.dragCurrent){ return; }
18156         var dds = Roo.dd.ScrollManager;
18157         if(!dragEl || dragEl != ddm.dragCurrent){
18158             dragEl = ddm.dragCurrent;
18159             // refresh regions on drag start
18160             dds.refreshCache();
18161         }
18162         
18163         var xy = Roo.lib.Event.getXY(e);
18164         var pt = new Roo.lib.Point(xy[0], xy[1]);
18165         for(var id in els){
18166             var el = els[id], r = el._region;
18167             if(r && r.contains(pt) && el.isScrollable()){
18168                 if(r.bottom - pt.y <= dds.thresh){
18169                     if(proc.el != el){
18170                         startProc(el, "down");
18171                     }
18172                     return;
18173                 }else if(r.right - pt.x <= dds.thresh){
18174                     if(proc.el != el){
18175                         startProc(el, "left");
18176                     }
18177                     return;
18178                 }else if(pt.y - r.top <= dds.thresh){
18179                     if(proc.el != el){
18180                         startProc(el, "up");
18181                     }
18182                     return;
18183                 }else if(pt.x - r.left <= dds.thresh){
18184                     if(proc.el != el){
18185                         startProc(el, "right");
18186                     }
18187                     return;
18188                 }
18189             }
18190         }
18191         clearProc();
18192     };
18193     
18194     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
18195     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
18196     
18197     return {
18198         /**
18199          * Registers new overflow element(s) to auto scroll
18200          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
18201          */
18202         register : function(el){
18203             if(el instanceof Array){
18204                 for(var i = 0, len = el.length; i < len; i++) {
18205                         this.register(el[i]);
18206                 }
18207             }else{
18208                 el = Roo.get(el);
18209                 els[el.id] = el;
18210             }
18211             Roo.dd.ScrollManager.els = els;
18212         },
18213         
18214         /**
18215          * Unregisters overflow element(s) so they are no longer scrolled
18216          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
18217          */
18218         unregister : function(el){
18219             if(el instanceof Array){
18220                 for(var i = 0, len = el.length; i < len; i++) {
18221                         this.unregister(el[i]);
18222                 }
18223             }else{
18224                 el = Roo.get(el);
18225                 delete els[el.id];
18226             }
18227         },
18228         
18229         /**
18230          * The number of pixels from the edge of a container the pointer needs to be to 
18231          * trigger scrolling (defaults to 25)
18232          * @type Number
18233          */
18234         thresh : 25,
18235         
18236         /**
18237          * The number of pixels to scroll in each scroll increment (defaults to 50)
18238          * @type Number
18239          */
18240         increment : 100,
18241         
18242         /**
18243          * The frequency of scrolls in milliseconds (defaults to 500)
18244          * @type Number
18245          */
18246         frequency : 500,
18247         
18248         /**
18249          * True to animate the scroll (defaults to true)
18250          * @type Boolean
18251          */
18252         animate: true,
18253         
18254         /**
18255          * The animation duration in seconds - 
18256          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
18257          * @type Number
18258          */
18259         animDuration: .4,
18260         
18261         /**
18262          * Manually trigger a cache refresh.
18263          */
18264         refreshCache : function(){
18265             for(var id in els){
18266                 if(typeof els[id] == 'object'){ // for people extending the object prototype
18267                     els[id]._region = els[id].getRegion();
18268                 }
18269             }
18270         }
18271     };
18272 }();/*
18273  * Based on:
18274  * Ext JS Library 1.1.1
18275  * Copyright(c) 2006-2007, Ext JS, LLC.
18276  *
18277  * Originally Released Under LGPL - original licence link has changed is not relivant.
18278  *
18279  * Fork - LGPL
18280  * <script type="text/javascript">
18281  */
18282  
18283
18284 /**
18285  * @class Roo.dd.Registry
18286  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
18287  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
18288  * @singleton
18289  */
18290 Roo.dd.Registry = function(){
18291     var elements = {}; 
18292     var handles = {}; 
18293     var autoIdSeed = 0;
18294
18295     var getId = function(el, autogen){
18296         if(typeof el == "string"){
18297             return el;
18298         }
18299         var id = el.id;
18300         if(!id && autogen !== false){
18301             id = "roodd-" + (++autoIdSeed);
18302             el.id = id;
18303         }
18304         return id;
18305     };
18306     
18307     return {
18308     /**
18309      * Register a drag drop element
18310      * @param {String|HTMLElement} element The id or DOM node to register
18311      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
18312      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
18313      * knows how to interpret, plus there are some specific properties known to the Registry that should be
18314      * populated in the data object (if applicable):
18315      * <pre>
18316 Value      Description<br />
18317 ---------  ------------------------------------------<br />
18318 handles    Array of DOM nodes that trigger dragging<br />
18319            for the element being registered<br />
18320 isHandle   True if the element passed in triggers<br />
18321            dragging itself, else false
18322 </pre>
18323      */
18324         register : function(el, data){
18325             data = data || {};
18326             if(typeof el == "string"){
18327                 el = document.getElementById(el);
18328             }
18329             data.ddel = el;
18330             elements[getId(el)] = data;
18331             if(data.isHandle !== false){
18332                 handles[data.ddel.id] = data;
18333             }
18334             if(data.handles){
18335                 var hs = data.handles;
18336                 for(var i = 0, len = hs.length; i < len; i++){
18337                         handles[getId(hs[i])] = data;
18338                 }
18339             }
18340         },
18341
18342     /**
18343      * Unregister a drag drop element
18344      * @param {String|HTMLElement}  element The id or DOM node to unregister
18345      */
18346         unregister : function(el){
18347             var id = getId(el, false);
18348             var data = elements[id];
18349             if(data){
18350                 delete elements[id];
18351                 if(data.handles){
18352                     var hs = data.handles;
18353                     for(var i = 0, len = hs.length; i < len; i++){
18354                         delete handles[getId(hs[i], false)];
18355                     }
18356                 }
18357             }
18358         },
18359
18360     /**
18361      * Returns the handle registered for a DOM Node by id
18362      * @param {String|HTMLElement} id The DOM node or id to look up
18363      * @return {Object} handle The custom handle data
18364      */
18365         getHandle : function(id){
18366             if(typeof id != "string"){ // must be element?
18367                 id = id.id;
18368             }
18369             return handles[id];
18370         },
18371
18372     /**
18373      * Returns the handle that is registered for the DOM node that is the target of the event
18374      * @param {Event} e The event
18375      * @return {Object} handle The custom handle data
18376      */
18377         getHandleFromEvent : function(e){
18378             var t = Roo.lib.Event.getTarget(e);
18379             return t ? handles[t.id] : null;
18380         },
18381
18382     /**
18383      * Returns a custom data object that is registered for a DOM node by id
18384      * @param {String|HTMLElement} id The DOM node or id to look up
18385      * @return {Object} data The custom data
18386      */
18387         getTarget : function(id){
18388             if(typeof id != "string"){ // must be element?
18389                 id = id.id;
18390             }
18391             return elements[id];
18392         },
18393
18394     /**
18395      * Returns a custom data object that is registered for the DOM node that is the target of the event
18396      * @param {Event} e The event
18397      * @return {Object} data The custom data
18398      */
18399         getTargetFromEvent : function(e){
18400             var t = Roo.lib.Event.getTarget(e);
18401             return t ? elements[t.id] || handles[t.id] : null;
18402         }
18403     };
18404 }();/*
18405  * Based on:
18406  * Ext JS Library 1.1.1
18407  * Copyright(c) 2006-2007, Ext JS, LLC.
18408  *
18409  * Originally Released Under LGPL - original licence link has changed is not relivant.
18410  *
18411  * Fork - LGPL
18412  * <script type="text/javascript">
18413  */
18414  
18415
18416 /**
18417  * @class Roo.dd.StatusProxy
18418  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
18419  * default drag proxy used by all Roo.dd components.
18420  * @constructor
18421  * @param {Object} config
18422  */
18423 Roo.dd.StatusProxy = function(config){
18424     Roo.apply(this, config);
18425     this.id = this.id || Roo.id();
18426     this.el = new Roo.Layer({
18427         dh: {
18428             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
18429                 {tag: "div", cls: "x-dd-drop-icon"},
18430                 {tag: "div", cls: "x-dd-drag-ghost"}
18431             ]
18432         }, 
18433         shadow: !config || config.shadow !== false
18434     });
18435     this.ghost = Roo.get(this.el.dom.childNodes[1]);
18436     this.dropStatus = this.dropNotAllowed;
18437 };
18438
18439 Roo.dd.StatusProxy.prototype = {
18440     /**
18441      * @cfg {String} dropAllowed
18442      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
18443      */
18444     dropAllowed : "x-dd-drop-ok",
18445     /**
18446      * @cfg {String} dropNotAllowed
18447      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
18448      */
18449     dropNotAllowed : "x-dd-drop-nodrop",
18450
18451     /**
18452      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
18453      * over the current target element.
18454      * @param {String} cssClass The css class for the new drop status indicator image
18455      */
18456     setStatus : function(cssClass){
18457         cssClass = cssClass || this.dropNotAllowed;
18458         if(this.dropStatus != cssClass){
18459             this.el.replaceClass(this.dropStatus, cssClass);
18460             this.dropStatus = cssClass;
18461         }
18462     },
18463
18464     /**
18465      * Resets the status indicator to the default dropNotAllowed value
18466      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
18467      */
18468     reset : function(clearGhost){
18469         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
18470         this.dropStatus = this.dropNotAllowed;
18471         if(clearGhost){
18472             this.ghost.update("");
18473         }
18474     },
18475
18476     /**
18477      * Updates the contents of the ghost element
18478      * @param {String} html The html that will replace the current innerHTML of the ghost element
18479      */
18480     update : function(html){
18481         if(typeof html == "string"){
18482             this.ghost.update(html);
18483         }else{
18484             this.ghost.update("");
18485             html.style.margin = "0";
18486             this.ghost.dom.appendChild(html);
18487         }
18488         // ensure float = none set?? cant remember why though.
18489         var el = this.ghost.dom.firstChild;
18490                 if(el){
18491                         Roo.fly(el).setStyle('float', 'none');
18492                 }
18493     },
18494     
18495     /**
18496      * Returns the underlying proxy {@link Roo.Layer}
18497      * @return {Roo.Layer} el
18498     */
18499     getEl : function(){
18500         return this.el;
18501     },
18502
18503     /**
18504      * Returns the ghost element
18505      * @return {Roo.Element} el
18506      */
18507     getGhost : function(){
18508         return this.ghost;
18509     },
18510
18511     /**
18512      * Hides the proxy
18513      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
18514      */
18515     hide : function(clear){
18516         this.el.hide();
18517         if(clear){
18518             this.reset(true);
18519         }
18520     },
18521
18522     /**
18523      * Stops the repair animation if it's currently running
18524      */
18525     stop : function(){
18526         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
18527             this.anim.stop();
18528         }
18529     },
18530
18531     /**
18532      * Displays this proxy
18533      */
18534     show : function(){
18535         this.el.show();
18536     },
18537
18538     /**
18539      * Force the Layer to sync its shadow and shim positions to the element
18540      */
18541     sync : function(){
18542         this.el.sync();
18543     },
18544
18545     /**
18546      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
18547      * invalid drop operation by the item being dragged.
18548      * @param {Array} xy The XY position of the element ([x, y])
18549      * @param {Function} callback The function to call after the repair is complete
18550      * @param {Object} scope The scope in which to execute the callback
18551      */
18552     repair : function(xy, callback, scope){
18553         this.callback = callback;
18554         this.scope = scope;
18555         if(xy && this.animRepair !== false){
18556             this.el.addClass("x-dd-drag-repair");
18557             this.el.hideUnders(true);
18558             this.anim = this.el.shift({
18559                 duration: this.repairDuration || .5,
18560                 easing: 'easeOut',
18561                 xy: xy,
18562                 stopFx: true,
18563                 callback: this.afterRepair,
18564                 scope: this
18565             });
18566         }else{
18567             this.afterRepair();
18568         }
18569     },
18570
18571     // private
18572     afterRepair : function(){
18573         this.hide(true);
18574         if(typeof this.callback == "function"){
18575             this.callback.call(this.scope || this);
18576         }
18577         this.callback = null;
18578         this.scope = null;
18579     }
18580 };/*
18581  * Based on:
18582  * Ext JS Library 1.1.1
18583  * Copyright(c) 2006-2007, Ext JS, LLC.
18584  *
18585  * Originally Released Under LGPL - original licence link has changed is not relivant.
18586  *
18587  * Fork - LGPL
18588  * <script type="text/javascript">
18589  */
18590
18591 /**
18592  * @class Roo.dd.DragSource
18593  * @extends Roo.dd.DDProxy
18594  * A simple class that provides the basic implementation needed to make any element draggable.
18595  * @constructor
18596  * @param {String/HTMLElement/Element} el The container element
18597  * @param {Object} config
18598  */
18599 Roo.dd.DragSource = function(el, config){
18600     this.el = Roo.get(el);
18601     this.dragData = {};
18602     
18603     Roo.apply(this, config);
18604     
18605     if(!this.proxy){
18606         this.proxy = new Roo.dd.StatusProxy();
18607     }
18608
18609     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18610           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18611     
18612     this.dragging = false;
18613 };
18614
18615 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18616     /**
18617      * @cfg {String} dropAllowed
18618      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18619      */
18620     dropAllowed : "x-dd-drop-ok",
18621     /**
18622      * @cfg {String} dropNotAllowed
18623      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18624      */
18625     dropNotAllowed : "x-dd-drop-nodrop",
18626
18627     /**
18628      * Returns the data object associated with this drag source
18629      * @return {Object} data An object containing arbitrary data
18630      */
18631     getDragData : function(e){
18632         return this.dragData;
18633     },
18634
18635     // private
18636     onDragEnter : function(e, id){
18637         var target = Roo.dd.DragDropMgr.getDDById(id);
18638         this.cachedTarget = target;
18639         if(this.beforeDragEnter(target, e, id) !== false){
18640             if(target.isNotifyTarget){
18641                 var status = target.notifyEnter(this, e, this.dragData);
18642                 this.proxy.setStatus(status);
18643             }else{
18644                 this.proxy.setStatus(this.dropAllowed);
18645             }
18646             
18647             if(this.afterDragEnter){
18648                 /**
18649                  * An empty function by default, but provided so that you can perform a custom action
18650                  * when the dragged item enters the drop target by providing an implementation.
18651                  * @param {Roo.dd.DragDrop} target The drop target
18652                  * @param {Event} e The event object
18653                  * @param {String} id The id of the dragged element
18654                  * @method afterDragEnter
18655                  */
18656                 this.afterDragEnter(target, e, id);
18657             }
18658         }
18659     },
18660
18661     /**
18662      * An empty function by default, but provided so that you can perform a custom action
18663      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18664      * @param {Roo.dd.DragDrop} target The drop target
18665      * @param {Event} e The event object
18666      * @param {String} id The id of the dragged element
18667      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18668      */
18669     beforeDragEnter : function(target, e, id){
18670         return true;
18671     },
18672
18673     // private
18674     alignElWithMouse: function() {
18675         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18676         this.proxy.sync();
18677     },
18678
18679     // private
18680     onDragOver : function(e, id){
18681         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18682         if(this.beforeDragOver(target, e, id) !== false){
18683             if(target.isNotifyTarget){
18684                 var status = target.notifyOver(this, e, this.dragData);
18685                 this.proxy.setStatus(status);
18686             }
18687
18688             if(this.afterDragOver){
18689                 /**
18690                  * An empty function by default, but provided so that you can perform a custom action
18691                  * while the dragged item is over the drop target by providing an implementation.
18692                  * @param {Roo.dd.DragDrop} target The drop target
18693                  * @param {Event} e The event object
18694                  * @param {String} id The id of the dragged element
18695                  * @method afterDragOver
18696                  */
18697                 this.afterDragOver(target, e, id);
18698             }
18699         }
18700     },
18701
18702     /**
18703      * An empty function by default, but provided so that you can perform a custom action
18704      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18705      * @param {Roo.dd.DragDrop} target The drop target
18706      * @param {Event} e The event object
18707      * @param {String} id The id of the dragged element
18708      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18709      */
18710     beforeDragOver : function(target, e, id){
18711         return true;
18712     },
18713
18714     // private
18715     onDragOut : function(e, id){
18716         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18717         if(this.beforeDragOut(target, e, id) !== false){
18718             if(target.isNotifyTarget){
18719                 target.notifyOut(this, e, this.dragData);
18720             }
18721             this.proxy.reset();
18722             if(this.afterDragOut){
18723                 /**
18724                  * An empty function by default, but provided so that you can perform a custom action
18725                  * after the dragged item is dragged out of the target without dropping.
18726                  * @param {Roo.dd.DragDrop} target The drop target
18727                  * @param {Event} e The event object
18728                  * @param {String} id The id of the dragged element
18729                  * @method afterDragOut
18730                  */
18731                 this.afterDragOut(target, e, id);
18732             }
18733         }
18734         this.cachedTarget = null;
18735     },
18736
18737     /**
18738      * An empty function by default, but provided so that you can perform a custom action before the dragged
18739      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18740      * @param {Roo.dd.DragDrop} target The drop target
18741      * @param {Event} e The event object
18742      * @param {String} id The id of the dragged element
18743      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18744      */
18745     beforeDragOut : function(target, e, id){
18746         return true;
18747     },
18748     
18749     // private
18750     onDragDrop : function(e, id){
18751         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18752         if(this.beforeDragDrop(target, e, id) !== false){
18753             if(target.isNotifyTarget){
18754                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18755                     this.onValidDrop(target, e, id);
18756                 }else{
18757                     this.onInvalidDrop(target, e, id);
18758                 }
18759             }else{
18760                 this.onValidDrop(target, e, id);
18761             }
18762             
18763             if(this.afterDragDrop){
18764                 /**
18765                  * An empty function by default, but provided so that you can perform a custom action
18766                  * after a valid drag drop has occurred by providing an implementation.
18767                  * @param {Roo.dd.DragDrop} target The drop target
18768                  * @param {Event} e The event object
18769                  * @param {String} id The id of the dropped element
18770                  * @method afterDragDrop
18771                  */
18772                 this.afterDragDrop(target, e, id);
18773             }
18774         }
18775         delete this.cachedTarget;
18776     },
18777
18778     /**
18779      * An empty function by default, but provided so that you can perform a custom action before the dragged
18780      * item is dropped onto the target and optionally cancel the onDragDrop.
18781      * @param {Roo.dd.DragDrop} target The drop target
18782      * @param {Event} e The event object
18783      * @param {String} id The id of the dragged element
18784      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18785      */
18786     beforeDragDrop : function(target, e, id){
18787         return true;
18788     },
18789
18790     // private
18791     onValidDrop : function(target, e, id){
18792         this.hideProxy();
18793         if(this.afterValidDrop){
18794             /**
18795              * An empty function by default, but provided so that you can perform a custom action
18796              * after a valid drop has occurred by providing an implementation.
18797              * @param {Object} target The target DD 
18798              * @param {Event} e The event object
18799              * @param {String} id The id of the dropped element
18800              * @method afterInvalidDrop
18801              */
18802             this.afterValidDrop(target, e, id);
18803         }
18804     },
18805
18806     // private
18807     getRepairXY : function(e, data){
18808         return this.el.getXY();  
18809     },
18810
18811     // private
18812     onInvalidDrop : function(target, e, id){
18813         this.beforeInvalidDrop(target, e, id);
18814         if(this.cachedTarget){
18815             if(this.cachedTarget.isNotifyTarget){
18816                 this.cachedTarget.notifyOut(this, e, this.dragData);
18817             }
18818             this.cacheTarget = null;
18819         }
18820         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18821
18822         if(this.afterInvalidDrop){
18823             /**
18824              * An empty function by default, but provided so that you can perform a custom action
18825              * after an invalid drop has occurred by providing an implementation.
18826              * @param {Event} e The event object
18827              * @param {String} id The id of the dropped element
18828              * @method afterInvalidDrop
18829              */
18830             this.afterInvalidDrop(e, id);
18831         }
18832     },
18833
18834     // private
18835     afterRepair : function(){
18836         if(Roo.enableFx){
18837             this.el.highlight(this.hlColor || "c3daf9");
18838         }
18839         this.dragging = false;
18840     },
18841
18842     /**
18843      * An empty function by default, but provided so that you can perform a custom action after an invalid
18844      * drop has occurred.
18845      * @param {Roo.dd.DragDrop} target The drop target
18846      * @param {Event} e The event object
18847      * @param {String} id The id of the dragged element
18848      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18849      */
18850     beforeInvalidDrop : function(target, e, id){
18851         return true;
18852     },
18853
18854     // private
18855     handleMouseDown : function(e){
18856         if(this.dragging) {
18857             return;
18858         }
18859         var data = this.getDragData(e);
18860         if(data && this.onBeforeDrag(data, e) !== false){
18861             this.dragData = data;
18862             this.proxy.stop();
18863             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18864         } 
18865     },
18866
18867     /**
18868      * An empty function by default, but provided so that you can perform a custom action before the initial
18869      * drag event begins and optionally cancel it.
18870      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18871      * @param {Event} e The event object
18872      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18873      */
18874     onBeforeDrag : function(data, e){
18875         return true;
18876     },
18877
18878     /**
18879      * An empty function by default, but provided so that you can perform a custom action once the initial
18880      * drag event has begun.  The drag cannot be canceled from this function.
18881      * @param {Number} x The x position of the click on the dragged object
18882      * @param {Number} y The y position of the click on the dragged object
18883      */
18884     onStartDrag : Roo.emptyFn,
18885
18886     // private - YUI override
18887     startDrag : function(x, y){
18888         this.proxy.reset();
18889         this.dragging = true;
18890         this.proxy.update("");
18891         this.onInitDrag(x, y);
18892         this.proxy.show();
18893     },
18894
18895     // private
18896     onInitDrag : function(x, y){
18897         var clone = this.el.dom.cloneNode(true);
18898         clone.id = Roo.id(); // prevent duplicate ids
18899         this.proxy.update(clone);
18900         this.onStartDrag(x, y);
18901         return true;
18902     },
18903
18904     /**
18905      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18906      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18907      */
18908     getProxy : function(){
18909         return this.proxy;  
18910     },
18911
18912     /**
18913      * Hides the drag source's {@link Roo.dd.StatusProxy}
18914      */
18915     hideProxy : function(){
18916         this.proxy.hide();  
18917         this.proxy.reset(true);
18918         this.dragging = false;
18919     },
18920
18921     // private
18922     triggerCacheRefresh : function(){
18923         Roo.dd.DDM.refreshCache(this.groups);
18924     },
18925
18926     // private - override to prevent hiding
18927     b4EndDrag: function(e) {
18928     },
18929
18930     // private - override to prevent moving
18931     endDrag : function(e){
18932         this.onEndDrag(this.dragData, e);
18933     },
18934
18935     // private
18936     onEndDrag : function(data, e){
18937     },
18938     
18939     // private - pin to cursor
18940     autoOffset : function(x, y) {
18941         this.setDelta(-12, -20);
18942     }    
18943 });/*
18944  * Based on:
18945  * Ext JS Library 1.1.1
18946  * Copyright(c) 2006-2007, Ext JS, LLC.
18947  *
18948  * Originally Released Under LGPL - original licence link has changed is not relivant.
18949  *
18950  * Fork - LGPL
18951  * <script type="text/javascript">
18952  */
18953
18954
18955 /**
18956  * @class Roo.dd.DropTarget
18957  * @extends Roo.dd.DDTarget
18958  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18959  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18960  * @constructor
18961  * @param {String/HTMLElement/Element} el The container element
18962  * @param {Object} config
18963  */
18964 Roo.dd.DropTarget = function(el, config){
18965     this.el = Roo.get(el);
18966     
18967     var listeners = false; ;
18968     if (config && config.listeners) {
18969         listeners= config.listeners;
18970         delete config.listeners;
18971     }
18972     Roo.apply(this, config);
18973     
18974     if(this.containerScroll){
18975         Roo.dd.ScrollManager.register(this.el);
18976     }
18977     this.addEvents( {
18978          /**
18979          * @scope Roo.dd.DropTarget
18980          */
18981          
18982          /**
18983          * @event enter
18984          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18985          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18986          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18987          * 
18988          * IMPORTANT : it should set this.overClass and this.dropAllowed
18989          * 
18990          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18991          * @param {Event} e The event
18992          * @param {Object} data An object containing arbitrary data supplied by the drag source
18993          */
18994         "enter" : true,
18995         
18996          /**
18997          * @event over
18998          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18999          * This method will be called on every mouse movement while the drag source is over the drop target.
19000          * This default implementation simply returns the dropAllowed config value.
19001          * 
19002          * IMPORTANT : it should set this.dropAllowed
19003          * 
19004          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
19005          * @param {Event} e The event
19006          * @param {Object} data An object containing arbitrary data supplied by the drag source
19007          
19008          */
19009         "over" : true,
19010         /**
19011          * @event out
19012          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
19013          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
19014          * overClass (if any) from the drop element.
19015          * 
19016          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
19017          * @param {Event} e The event
19018          * @param {Object} data An object containing arbitrary data supplied by the drag source
19019          */
19020          "out" : true,
19021          
19022         /**
19023          * @event drop
19024          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
19025          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
19026          * implementation that does something to process the drop event and returns true so that the drag source's
19027          * repair action does not run.
19028          * 
19029          * IMPORTANT : it should set this.success
19030          * 
19031          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
19032          * @param {Event} e The event
19033          * @param {Object} data An object containing arbitrary data supplied by the drag source
19034         */
19035          "drop" : true
19036     });
19037             
19038      
19039     Roo.dd.DropTarget.superclass.constructor.call(  this, 
19040         this.el.dom, 
19041         this.ddGroup || this.group,
19042         {
19043             isTarget: true,
19044             listeners : listeners || {} 
19045            
19046         
19047         }
19048     );
19049
19050 };
19051
19052 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
19053     /**
19054      * @cfg {String} overClass
19055      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
19056      */
19057      /**
19058      * @cfg {String} ddGroup
19059      * The drag drop group to handle drop events for
19060      */
19061      
19062     /**
19063      * @cfg {String} dropAllowed
19064      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19065      */
19066     dropAllowed : "x-dd-drop-ok",
19067     /**
19068      * @cfg {String} dropNotAllowed
19069      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19070      */
19071     dropNotAllowed : "x-dd-drop-nodrop",
19072     /**
19073      * @cfg {boolean} success
19074      * set this after drop listener.. 
19075      */
19076     success : false,
19077     /**
19078      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
19079      * if the drop point is valid for over/enter..
19080      */
19081     valid : false,
19082     // private
19083     isTarget : true,
19084
19085     // private
19086     isNotifyTarget : true,
19087     
19088     /**
19089      * @hide
19090      */
19091     notifyEnter : function(dd, e, data)
19092     {
19093         this.valid = true;
19094         this.fireEvent('enter', dd, e, data);
19095         if(this.overClass){
19096             this.el.addClass(this.overClass);
19097         }
19098         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
19099             this.valid ? this.dropAllowed : this.dropNotAllowed
19100         );
19101     },
19102
19103     /**
19104      * @hide
19105      */
19106     notifyOver : function(dd, e, data)
19107     {
19108         this.valid = true;
19109         this.fireEvent('over', dd, e, data);
19110         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
19111             this.valid ? this.dropAllowed : this.dropNotAllowed
19112         );
19113     },
19114
19115     /**
19116      * @hide
19117      */
19118     notifyOut : function(dd, e, data)
19119     {
19120         this.fireEvent('out', dd, e, data);
19121         if(this.overClass){
19122             this.el.removeClass(this.overClass);
19123         }
19124     },
19125
19126     /**
19127      * @hide
19128      */
19129     notifyDrop : function(dd, e, data)
19130     {
19131         this.success = false;
19132         this.fireEvent('drop', dd, e, data);
19133         return this.success;
19134     }
19135 });/*
19136  * Based on:
19137  * Ext JS Library 1.1.1
19138  * Copyright(c) 2006-2007, Ext JS, LLC.
19139  *
19140  * Originally Released Under LGPL - original licence link has changed is not relivant.
19141  *
19142  * Fork - LGPL
19143  * <script type="text/javascript">
19144  */
19145
19146
19147 /**
19148  * @class Roo.dd.DragZone
19149  * @extends Roo.dd.DragSource
19150  * This class provides a container DD instance that proxies for multiple child node sources.<br />
19151  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
19152  * @constructor
19153  * @param {String/HTMLElement/Element} el The container element
19154  * @param {Object} config
19155  */
19156 Roo.dd.DragZone = function(el, config){
19157     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
19158     if(this.containerScroll){
19159         Roo.dd.ScrollManager.register(this.el);
19160     }
19161 };
19162
19163 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
19164     /**
19165      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
19166      * for auto scrolling during drag operations.
19167      */
19168     /**
19169      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
19170      * method after a failed drop (defaults to "c3daf9" - light blue)
19171      */
19172
19173     /**
19174      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
19175      * for a valid target to drag based on the mouse down. Override this method
19176      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
19177      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
19178      * @param {EventObject} e The mouse down event
19179      * @return {Object} The dragData
19180      */
19181     getDragData : function(e){
19182         return Roo.dd.Registry.getHandleFromEvent(e);
19183     },
19184     
19185     /**
19186      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
19187      * this.dragData.ddel
19188      * @param {Number} x The x position of the click on the dragged object
19189      * @param {Number} y The y position of the click on the dragged object
19190      * @return {Boolean} true to continue the drag, false to cancel
19191      */
19192     onInitDrag : function(x, y){
19193         this.proxy.update(this.dragData.ddel.cloneNode(true));
19194         this.onStartDrag(x, y);
19195         return true;
19196     },
19197     
19198     /**
19199      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
19200      */
19201     afterRepair : function(){
19202         if(Roo.enableFx){
19203             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
19204         }
19205         this.dragging = false;
19206     },
19207
19208     /**
19209      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
19210      * the XY of this.dragData.ddel
19211      * @param {EventObject} e The mouse up event
19212      * @return {Array} The xy location (e.g. [100, 200])
19213      */
19214     getRepairXY : function(e){
19215         return Roo.Element.fly(this.dragData.ddel).getXY();  
19216     }
19217 });/*
19218  * Based on:
19219  * Ext JS Library 1.1.1
19220  * Copyright(c) 2006-2007, Ext JS, LLC.
19221  *
19222  * Originally Released Under LGPL - original licence link has changed is not relivant.
19223  *
19224  * Fork - LGPL
19225  * <script type="text/javascript">
19226  */
19227 /**
19228  * @class Roo.dd.DropZone
19229  * @extends Roo.dd.DropTarget
19230  * This class provides a container DD instance that proxies for multiple child node targets.<br />
19231  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
19232  * @constructor
19233  * @param {String/HTMLElement/Element} el The container element
19234  * @param {Object} config
19235  */
19236 Roo.dd.DropZone = function(el, config){
19237     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
19238 };
19239
19240 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
19241     /**
19242      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
19243      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
19244      * provide your own custom lookup.
19245      * @param {Event} e The event
19246      * @return {Object} data The custom data
19247      */
19248     getTargetFromEvent : function(e){
19249         return Roo.dd.Registry.getTargetFromEvent(e);
19250     },
19251
19252     /**
19253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
19254      * that it has registered.  This method has no default implementation and should be overridden to provide
19255      * node-specific processing if necessary.
19256      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
19257      * {@link #getTargetFromEvent} for this node)
19258      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19259      * @param {Event} e The event
19260      * @param {Object} data An object containing arbitrary data supplied by the drag source
19261      */
19262     onNodeEnter : function(n, dd, e, data){
19263         
19264     },
19265
19266     /**
19267      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
19268      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
19269      * overridden to provide the proper feedback.
19270      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19271      * {@link #getTargetFromEvent} for this node)
19272      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19273      * @param {Event} e The event
19274      * @param {Object} data An object containing arbitrary data supplied by the drag source
19275      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19276      * underlying {@link Roo.dd.StatusProxy} can be updated
19277      */
19278     onNodeOver : function(n, dd, e, data){
19279         return this.dropAllowed;
19280     },
19281
19282     /**
19283      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
19284      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
19285      * node-specific processing if necessary.
19286      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19287      * {@link #getTargetFromEvent} for this node)
19288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19289      * @param {Event} e The event
19290      * @param {Object} data An object containing arbitrary data supplied by the drag source
19291      */
19292     onNodeOut : function(n, dd, e, data){
19293         
19294     },
19295
19296     /**
19297      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
19298      * the drop node.  The default implementation returns false, so it should be overridden to provide the
19299      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
19300      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19301      * {@link #getTargetFromEvent} for this node)
19302      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19303      * @param {Event} e The event
19304      * @param {Object} data An object containing arbitrary data supplied by the drag source
19305      * @return {Boolean} True if the drop was valid, else false
19306      */
19307     onNodeDrop : function(n, dd, e, data){
19308         return false;
19309     },
19310
19311     /**
19312      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
19313      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
19314      * it should be overridden to provide the proper feedback if necessary.
19315      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19316      * @param {Event} e The event
19317      * @param {Object} data An object containing arbitrary data supplied by the drag source
19318      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19319      * underlying {@link Roo.dd.StatusProxy} can be updated
19320      */
19321     onContainerOver : function(dd, e, data){
19322         return this.dropNotAllowed;
19323     },
19324
19325     /**
19326      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
19327      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
19328      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
19329      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
19330      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19331      * @param {Event} e The event
19332      * @param {Object} data An object containing arbitrary data supplied by the drag source
19333      * @return {Boolean} True if the drop was valid, else false
19334      */
19335     onContainerDrop : function(dd, e, data){
19336         return false;
19337     },
19338
19339     /**
19340      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
19341      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
19342      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
19343      * you should override this method and provide a custom implementation.
19344      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19345      * @param {Event} e The event
19346      * @param {Object} data An object containing arbitrary data supplied by the drag source
19347      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19348      * underlying {@link Roo.dd.StatusProxy} can be updated
19349      */
19350     notifyEnter : function(dd, e, data){
19351         return this.dropNotAllowed;
19352     },
19353
19354     /**
19355      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
19356      * This method will be called on every mouse movement while the drag source is over the drop zone.
19357      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
19358      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
19359      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
19360      * registered node, it will call {@link #onContainerOver}.
19361      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19362      * @param {Event} e The event
19363      * @param {Object} data An object containing arbitrary data supplied by the drag source
19364      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19365      * underlying {@link Roo.dd.StatusProxy} can be updated
19366      */
19367     notifyOver : function(dd, e, data){
19368         var n = this.getTargetFromEvent(e);
19369         if(!n){ // not over valid drop target
19370             if(this.lastOverNode){
19371                 this.onNodeOut(this.lastOverNode, dd, e, data);
19372                 this.lastOverNode = null;
19373             }
19374             return this.onContainerOver(dd, e, data);
19375         }
19376         if(this.lastOverNode != n){
19377             if(this.lastOverNode){
19378                 this.onNodeOut(this.lastOverNode, dd, e, data);
19379             }
19380             this.onNodeEnter(n, dd, e, data);
19381             this.lastOverNode = n;
19382         }
19383         return this.onNodeOver(n, dd, e, data);
19384     },
19385
19386     /**
19387      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
19388      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
19389      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
19390      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
19391      * @param {Event} e The event
19392      * @param {Object} data An object containing arbitrary data supplied by the drag zone
19393      */
19394     notifyOut : function(dd, e, data){
19395         if(this.lastOverNode){
19396             this.onNodeOut(this.lastOverNode, dd, e, data);
19397             this.lastOverNode = null;
19398         }
19399     },
19400
19401     /**
19402      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
19403      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
19404      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
19405      * otherwise it will call {@link #onContainerDrop}.
19406      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19407      * @param {Event} e The event
19408      * @param {Object} data An object containing arbitrary data supplied by the drag source
19409      * @return {Boolean} True if the drop was valid, else false
19410      */
19411     notifyDrop : function(dd, e, data){
19412         if(this.lastOverNode){
19413             this.onNodeOut(this.lastOverNode, dd, e, data);
19414             this.lastOverNode = null;
19415         }
19416         var n = this.getTargetFromEvent(e);
19417         return n ?
19418             this.onNodeDrop(n, dd, e, data) :
19419             this.onContainerDrop(dd, e, data);
19420     },
19421
19422     // private
19423     triggerCacheRefresh : function(){
19424         Roo.dd.DDM.refreshCache(this.groups);
19425     }  
19426 });/*
19427  * Based on:
19428  * Ext JS Library 1.1.1
19429  * Copyright(c) 2006-2007, Ext JS, LLC.
19430  *
19431  * Originally Released Under LGPL - original licence link has changed is not relivant.
19432  *
19433  * Fork - LGPL
19434  * <script type="text/javascript">
19435  */
19436
19437
19438 /**
19439  * @class Roo.data.SortTypes
19440  * @singleton
19441  * Defines the default sorting (casting?) comparison functions used when sorting data.
19442  */
19443 Roo.data.SortTypes = {
19444     /**
19445      * Default sort that does nothing
19446      * @param {Mixed} s The value being converted
19447      * @return {Mixed} The comparison value
19448      */
19449     none : function(s){
19450         return s;
19451     },
19452     
19453     /**
19454      * The regular expression used to strip tags
19455      * @type {RegExp}
19456      * @property
19457      */
19458     stripTagsRE : /<\/?[^>]+>/gi,
19459     
19460     /**
19461      * Strips all HTML tags to sort on text only
19462      * @param {Mixed} s The value being converted
19463      * @return {String} The comparison value
19464      */
19465     asText : function(s){
19466         return String(s).replace(this.stripTagsRE, "");
19467     },
19468     
19469     /**
19470      * Strips all HTML tags to sort on text only - Case insensitive
19471      * @param {Mixed} s The value being converted
19472      * @return {String} The comparison value
19473      */
19474     asUCText : function(s){
19475         return String(s).toUpperCase().replace(this.stripTagsRE, "");
19476     },
19477     
19478     /**
19479      * Case insensitive string
19480      * @param {Mixed} s The value being converted
19481      * @return {String} The comparison value
19482      */
19483     asUCString : function(s) {
19484         return String(s).toUpperCase();
19485     },
19486     
19487     /**
19488      * Date sorting
19489      * @param {Mixed} s The value being converted
19490      * @return {Number} The comparison value
19491      */
19492     asDate : function(s) {
19493         if(!s){
19494             return 0;
19495         }
19496         if(s instanceof Date){
19497             return s.getTime();
19498         }
19499         return Date.parse(String(s));
19500     },
19501     
19502     /**
19503      * Float sorting
19504      * @param {Mixed} s The value being converted
19505      * @return {Float} The comparison value
19506      */
19507     asFloat : function(s) {
19508         var val = parseFloat(String(s).replace(/,/g, ""));
19509         if(isNaN(val)) val = 0;
19510         return val;
19511     },
19512     
19513     /**
19514      * Integer sorting
19515      * @param {Mixed} s The value being converted
19516      * @return {Number} The comparison value
19517      */
19518     asInt : function(s) {
19519         var val = parseInt(String(s).replace(/,/g, ""));
19520         if(isNaN(val)) val = 0;
19521         return val;
19522     }
19523 };/*
19524  * Based on:
19525  * Ext JS Library 1.1.1
19526  * Copyright(c) 2006-2007, Ext JS, LLC.
19527  *
19528  * Originally Released Under LGPL - original licence link has changed is not relivant.
19529  *
19530  * Fork - LGPL
19531  * <script type="text/javascript">
19532  */
19533
19534 /**
19535 * @class Roo.data.Record
19536  * Instances of this class encapsulate both record <em>definition</em> information, and record
19537  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
19538  * to access Records cached in an {@link Roo.data.Store} object.<br>
19539  * <p>
19540  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
19541  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
19542  * objects.<br>
19543  * <p>
19544  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
19545  * @constructor
19546  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
19547  * {@link #create}. The parameters are the same.
19548  * @param {Array} data An associative Array of data values keyed by the field name.
19549  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
19550  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
19551  * not specified an integer id is generated.
19552  */
19553 Roo.data.Record = function(data, id){
19554     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
19555     this.data = data;
19556 };
19557
19558 /**
19559  * Generate a constructor for a specific record layout.
19560  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
19561  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
19562  * Each field definition object may contain the following properties: <ul>
19563  * <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,
19564  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
19565  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
19566  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
19567  * is being used, then this is a string containing the javascript expression to reference the data relative to 
19568  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
19569  * to the data item relative to the record element. If the mapping expression is the same as the field name,
19570  * this may be omitted.</p></li>
19571  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
19572  * <ul><li>auto (Default, implies no conversion)</li>
19573  * <li>string</li>
19574  * <li>int</li>
19575  * <li>float</li>
19576  * <li>boolean</li>
19577  * <li>date</li></ul></p></li>
19578  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
19579  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19580  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19581  * by the Reader into an object that will be stored in the Record. It is passed the
19582  * following parameters:<ul>
19583  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19584  * </ul></p></li>
19585  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19586  * </ul>
19587  * <br>usage:<br><pre><code>
19588 var TopicRecord = Roo.data.Record.create(
19589     {name: 'title', mapping: 'topic_title'},
19590     {name: 'author', mapping: 'username'},
19591     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19592     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19593     {name: 'lastPoster', mapping: 'user2'},
19594     {name: 'excerpt', mapping: 'post_text'}
19595 );
19596
19597 var myNewRecord = new TopicRecord({
19598     title: 'Do my job please',
19599     author: 'noobie',
19600     totalPosts: 1,
19601     lastPost: new Date(),
19602     lastPoster: 'Animal',
19603     excerpt: 'No way dude!'
19604 });
19605 myStore.add(myNewRecord);
19606 </code></pre>
19607  * @method create
19608  * @static
19609  */
19610 Roo.data.Record.create = function(o){
19611     var f = function(){
19612         f.superclass.constructor.apply(this, arguments);
19613     };
19614     Roo.extend(f, Roo.data.Record);
19615     var p = f.prototype;
19616     p.fields = new Roo.util.MixedCollection(false, function(field){
19617         return field.name;
19618     });
19619     for(var i = 0, len = o.length; i < len; i++){
19620         p.fields.add(new Roo.data.Field(o[i]));
19621     }
19622     f.getField = function(name){
19623         return p.fields.get(name);  
19624     };
19625     return f;
19626 };
19627
19628 Roo.data.Record.AUTO_ID = 1000;
19629 Roo.data.Record.EDIT = 'edit';
19630 Roo.data.Record.REJECT = 'reject';
19631 Roo.data.Record.COMMIT = 'commit';
19632
19633 Roo.data.Record.prototype = {
19634     /**
19635      * Readonly flag - true if this record has been modified.
19636      * @type Boolean
19637      */
19638     dirty : false,
19639     editing : false,
19640     error: null,
19641     modified: null,
19642
19643     // private
19644     join : function(store){
19645         this.store = store;
19646     },
19647
19648     /**
19649      * Set the named field to the specified value.
19650      * @param {String} name The name of the field to set.
19651      * @param {Object} value The value to set the field to.
19652      */
19653     set : function(name, value){
19654         if(this.data[name] == value){
19655             return;
19656         }
19657         this.dirty = true;
19658         if(!this.modified){
19659             this.modified = {};
19660         }
19661         if(typeof this.modified[name] == 'undefined'){
19662             this.modified[name] = this.data[name];
19663         }
19664         this.data[name] = value;
19665         if(!this.editing && this.store){
19666             this.store.afterEdit(this);
19667         }       
19668     },
19669
19670     /**
19671      * Get the value of the named field.
19672      * @param {String} name The name of the field to get the value of.
19673      * @return {Object} The value of the field.
19674      */
19675     get : function(name){
19676         return this.data[name]; 
19677     },
19678
19679     // private
19680     beginEdit : function(){
19681         this.editing = true;
19682         this.modified = {}; 
19683     },
19684
19685     // private
19686     cancelEdit : function(){
19687         this.editing = false;
19688         delete this.modified;
19689     },
19690
19691     // private
19692     endEdit : function(){
19693         this.editing = false;
19694         if(this.dirty && this.store){
19695             this.store.afterEdit(this);
19696         }
19697     },
19698
19699     /**
19700      * Usually called by the {@link Roo.data.Store} which owns the Record.
19701      * Rejects all changes made to the Record since either creation, or the last commit operation.
19702      * Modified fields are reverted to their original values.
19703      * <p>
19704      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19705      * of reject operations.
19706      */
19707     reject : function(){
19708         var m = this.modified;
19709         for(var n in m){
19710             if(typeof m[n] != "function"){
19711                 this.data[n] = m[n];
19712             }
19713         }
19714         this.dirty = false;
19715         delete this.modified;
19716         this.editing = false;
19717         if(this.store){
19718             this.store.afterReject(this);
19719         }
19720     },
19721
19722     /**
19723      * Usually called by the {@link Roo.data.Store} which owns the Record.
19724      * Commits all changes made to the Record since either creation, or the last commit operation.
19725      * <p>
19726      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19727      * of commit operations.
19728      */
19729     commit : function(){
19730         this.dirty = false;
19731         delete this.modified;
19732         this.editing = false;
19733         if(this.store){
19734             this.store.afterCommit(this);
19735         }
19736     },
19737
19738     // private
19739     hasError : function(){
19740         return this.error != null;
19741     },
19742
19743     // private
19744     clearError : function(){
19745         this.error = null;
19746     },
19747
19748     /**
19749      * Creates a copy of this record.
19750      * @param {String} id (optional) A new record id if you don't want to use this record's id
19751      * @return {Record}
19752      */
19753     copy : function(newId) {
19754         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19755     }
19756 };/*
19757  * Based on:
19758  * Ext JS Library 1.1.1
19759  * Copyright(c) 2006-2007, Ext JS, LLC.
19760  *
19761  * Originally Released Under LGPL - original licence link has changed is not relivant.
19762  *
19763  * Fork - LGPL
19764  * <script type="text/javascript">
19765  */
19766
19767
19768
19769 /**
19770  * @class Roo.data.Store
19771  * @extends Roo.util.Observable
19772  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19773  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19774  * <p>
19775  * 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
19776  * has no knowledge of the format of the data returned by the Proxy.<br>
19777  * <p>
19778  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19779  * instances from the data object. These records are cached and made available through accessor functions.
19780  * @constructor
19781  * Creates a new Store.
19782  * @param {Object} config A config object containing the objects needed for the Store to access data,
19783  * and read the data into Records.
19784  */
19785 Roo.data.Store = function(config){
19786     this.data = new Roo.util.MixedCollection(false);
19787     this.data.getKey = function(o){
19788         return o.id;
19789     };
19790     this.baseParams = {};
19791     // private
19792     this.paramNames = {
19793         "start" : "start",
19794         "limit" : "limit",
19795         "sort" : "sort",
19796         "dir" : "dir",
19797         "multisort" : "_multisort"
19798     };
19799
19800     if(config && config.data){
19801         this.inlineData = config.data;
19802         delete config.data;
19803     }
19804
19805     Roo.apply(this, config);
19806     
19807     if(this.reader){ // reader passed
19808         this.reader = Roo.factory(this.reader, Roo.data);
19809         this.reader.xmodule = this.xmodule || false;
19810         if(!this.recordType){
19811             this.recordType = this.reader.recordType;
19812         }
19813         if(this.reader.onMetaChange){
19814             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19815         }
19816     }
19817
19818     if(this.recordType){
19819         this.fields = this.recordType.prototype.fields;
19820     }
19821     this.modified = [];
19822
19823     this.addEvents({
19824         /**
19825          * @event datachanged
19826          * Fires when the data cache has changed, and a widget which is using this Store
19827          * as a Record cache should refresh its view.
19828          * @param {Store} this
19829          */
19830         datachanged : true,
19831         /**
19832          * @event metachange
19833          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19834          * @param {Store} this
19835          * @param {Object} meta The JSON metadata
19836          */
19837         metachange : true,
19838         /**
19839          * @event add
19840          * Fires when Records have been added to the Store
19841          * @param {Store} this
19842          * @param {Roo.data.Record[]} records The array of Records added
19843          * @param {Number} index The index at which the record(s) were added
19844          */
19845         add : true,
19846         /**
19847          * @event remove
19848          * Fires when a Record has been removed from the Store
19849          * @param {Store} this
19850          * @param {Roo.data.Record} record The Record that was removed
19851          * @param {Number} index The index at which the record was removed
19852          */
19853         remove : true,
19854         /**
19855          * @event update
19856          * Fires when a Record has been updated
19857          * @param {Store} this
19858          * @param {Roo.data.Record} record The Record that was updated
19859          * @param {String} operation The update operation being performed.  Value may be one of:
19860          * <pre><code>
19861  Roo.data.Record.EDIT
19862  Roo.data.Record.REJECT
19863  Roo.data.Record.COMMIT
19864          * </code></pre>
19865          */
19866         update : true,
19867         /**
19868          * @event clear
19869          * Fires when the data cache has been cleared.
19870          * @param {Store} this
19871          */
19872         clear : true,
19873         /**
19874          * @event beforeload
19875          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19876          * the load action will be canceled.
19877          * @param {Store} this
19878          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19879          */
19880         beforeload : true,
19881         /**
19882          * @event beforeloadadd
19883          * Fires after a new set of Records has been loaded.
19884          * @param {Store} this
19885          * @param {Roo.data.Record[]} records The Records that were loaded
19886          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19887          */
19888         beforeloadadd : true,
19889         /**
19890          * @event load
19891          * Fires after a new set of Records has been loaded, before they are added to the store.
19892          * @param {Store} this
19893          * @param {Roo.data.Record[]} records The Records that were loaded
19894          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19895          * @params {Object} return from reader
19896          */
19897         load : true,
19898         /**
19899          * @event loadexception
19900          * Fires if an exception occurs in the Proxy during loading.
19901          * Called with the signature of the Proxy's "loadexception" event.
19902          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19903          * 
19904          * @param {Proxy} 
19905          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19906          * @param {Object} load options 
19907          * @param {Object} jsonData from your request (normally this contains the Exception)
19908          */
19909         loadexception : true
19910     });
19911     
19912     if(this.proxy){
19913         this.proxy = Roo.factory(this.proxy, Roo.data);
19914         this.proxy.xmodule = this.xmodule || false;
19915         this.relayEvents(this.proxy,  ["loadexception"]);
19916     }
19917     this.sortToggle = {};
19918     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19919
19920     Roo.data.Store.superclass.constructor.call(this);
19921
19922     if(this.inlineData){
19923         this.loadData(this.inlineData);
19924         delete this.inlineData;
19925     }
19926 };
19927
19928 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19929      /**
19930     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19931     * without a remote query - used by combo/forms at present.
19932     */
19933     
19934     /**
19935     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19936     */
19937     /**
19938     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19939     */
19940     /**
19941     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19942     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19943     */
19944     /**
19945     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19946     * on any HTTP request
19947     */
19948     /**
19949     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19950     */
19951     /**
19952     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19953     */
19954     multiSort: false,
19955     /**
19956     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19957     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19958     */
19959     remoteSort : false,
19960
19961     /**
19962     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19963      * loaded or when a record is removed. (defaults to false).
19964     */
19965     pruneModifiedRecords : false,
19966
19967     // private
19968     lastOptions : null,
19969
19970     /**
19971      * Add Records to the Store and fires the add event.
19972      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19973      */
19974     add : function(records){
19975         records = [].concat(records);
19976         for(var i = 0, len = records.length; i < len; i++){
19977             records[i].join(this);
19978         }
19979         var index = this.data.length;
19980         this.data.addAll(records);
19981         this.fireEvent("add", this, records, index);
19982     },
19983
19984     /**
19985      * Remove a Record from the Store and fires the remove event.
19986      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19987      */
19988     remove : function(record){
19989         var index = this.data.indexOf(record);
19990         this.data.removeAt(index);
19991         if(this.pruneModifiedRecords){
19992             this.modified.remove(record);
19993         }
19994         this.fireEvent("remove", this, record, index);
19995     },
19996
19997     /**
19998      * Remove all Records from the Store and fires the clear event.
19999      */
20000     removeAll : function(){
20001         this.data.clear();
20002         if(this.pruneModifiedRecords){
20003             this.modified = [];
20004         }
20005         this.fireEvent("clear", this);
20006     },
20007
20008     /**
20009      * Inserts Records to the Store at the given index and fires the add event.
20010      * @param {Number} index The start index at which to insert the passed Records.
20011      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
20012      */
20013     insert : function(index, records){
20014         records = [].concat(records);
20015         for(var i = 0, len = records.length; i < len; i++){
20016             this.data.insert(index, records[i]);
20017             records[i].join(this);
20018         }
20019         this.fireEvent("add", this, records, index);
20020     },
20021
20022     /**
20023      * Get the index within the cache of the passed Record.
20024      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
20025      * @return {Number} The index of the passed Record. Returns -1 if not found.
20026      */
20027     indexOf : function(record){
20028         return this.data.indexOf(record);
20029     },
20030
20031     /**
20032      * Get the index within the cache of the Record with the passed id.
20033      * @param {String} id The id of the Record to find.
20034      * @return {Number} The index of the Record. Returns -1 if not found.
20035      */
20036     indexOfId : function(id){
20037         return this.data.indexOfKey(id);
20038     },
20039
20040     /**
20041      * Get the Record with the specified id.
20042      * @param {String} id The id of the Record to find.
20043      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
20044      */
20045     getById : function(id){
20046         return this.data.key(id);
20047     },
20048
20049     /**
20050      * Get the Record at the specified index.
20051      * @param {Number} index The index of the Record to find.
20052      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
20053      */
20054     getAt : function(index){
20055         return this.data.itemAt(index);
20056     },
20057
20058     /**
20059      * Returns a range of Records between specified indices.
20060      * @param {Number} startIndex (optional) The starting index (defaults to 0)
20061      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
20062      * @return {Roo.data.Record[]} An array of Records
20063      */
20064     getRange : function(start, end){
20065         return this.data.getRange(start, end);
20066     },
20067
20068     // private
20069     storeOptions : function(o){
20070         o = Roo.apply({}, o);
20071         delete o.callback;
20072         delete o.scope;
20073         this.lastOptions = o;
20074     },
20075
20076     /**
20077      * Loads the Record cache from the configured Proxy using the configured Reader.
20078      * <p>
20079      * If using remote paging, then the first load call must specify the <em>start</em>
20080      * and <em>limit</em> properties in the options.params property to establish the initial
20081      * position within the dataset, and the number of Records to cache on each read from the Proxy.
20082      * <p>
20083      * <strong>It is important to note that for remote data sources, loading is asynchronous,
20084      * and this call will return before the new data has been loaded. Perform any post-processing
20085      * in a callback function, or in a "load" event handler.</strong>
20086      * <p>
20087      * @param {Object} options An object containing properties which control loading options:<ul>
20088      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
20089      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
20090      * passed the following arguments:<ul>
20091      * <li>r : Roo.data.Record[]</li>
20092      * <li>options: Options object from the load call</li>
20093      * <li>success: Boolean success indicator</li></ul></li>
20094      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
20095      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
20096      * </ul>
20097      */
20098     load : function(options){
20099         options = options || {};
20100         if(this.fireEvent("beforeload", this, options) !== false){
20101             this.storeOptions(options);
20102             var p = Roo.apply(options.params || {}, this.baseParams);
20103             // if meta was not loaded from remote source.. try requesting it.
20104             if (!this.reader.metaFromRemote) {
20105                 p._requestMeta = 1;
20106             }
20107             if(this.sortInfo && this.remoteSort){
20108                 var pn = this.paramNames;
20109                 p[pn["sort"]] = this.sortInfo.field;
20110                 p[pn["dir"]] = this.sortInfo.direction;
20111             }
20112             if (this.multiSort) {
20113                 var pn = this.paramNames;
20114                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
20115             }
20116             
20117             this.proxy.load(p, this.reader, this.loadRecords, this, options);
20118         }
20119     },
20120
20121     /**
20122      * Reloads the Record cache from the configured Proxy using the configured Reader and
20123      * the options from the last load operation performed.
20124      * @param {Object} options (optional) An object containing properties which may override the options
20125      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
20126      * the most recently used options are reused).
20127      */
20128     reload : function(options){
20129         this.load(Roo.applyIf(options||{}, this.lastOptions));
20130     },
20131
20132     // private
20133     // Called as a callback by the Reader during a load operation.
20134     loadRecords : function(o, options, success){
20135         if(!o || success === false){
20136             if(success !== false){
20137                 this.fireEvent("load", this, [], options, o);
20138             }
20139             if(options.callback){
20140                 options.callback.call(options.scope || this, [], options, false);
20141             }
20142             return;
20143         }
20144         // if data returned failure - throw an exception.
20145         if (o.success === false) {
20146             // show a message if no listener is registered.
20147             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
20148                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
20149             }
20150             // loadmask wil be hooked into this..
20151             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
20152             return;
20153         }
20154         var r = o.records, t = o.totalRecords || r.length;
20155         
20156         this.fireEvent("beforeloadadd", this, r, options, o);
20157         
20158         if(!options || options.add !== true){
20159             if(this.pruneModifiedRecords){
20160                 this.modified = [];
20161             }
20162             for(var i = 0, len = r.length; i < len; i++){
20163                 r[i].join(this);
20164             }
20165             if(this.snapshot){
20166                 this.data = this.snapshot;
20167                 delete this.snapshot;
20168             }
20169             this.data.clear();
20170             this.data.addAll(r);
20171             this.totalLength = t;
20172             this.applySort();
20173             this.fireEvent("datachanged", this);
20174         }else{
20175             this.totalLength = Math.max(t, this.data.length+r.length);
20176             this.add(r);
20177         }
20178         this.fireEvent("load", this, r, options, o);
20179         if(options.callback){
20180             options.callback.call(options.scope || this, r, options, true);
20181         }
20182     },
20183
20184
20185     /**
20186      * Loads data from a passed data block. A Reader which understands the format of the data
20187      * must have been configured in the constructor.
20188      * @param {Object} data The data block from which to read the Records.  The format of the data expected
20189      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
20190      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
20191      */
20192     loadData : function(o, append){
20193         var r = this.reader.readRecords(o);
20194         this.loadRecords(r, {add: append}, true);
20195     },
20196
20197     /**
20198      * Gets the number of cached records.
20199      * <p>
20200      * <em>If using paging, this may not be the total size of the dataset. If the data object
20201      * used by the Reader contains the dataset size, then the getTotalCount() function returns
20202      * the data set size</em>
20203      */
20204     getCount : function(){
20205         return this.data.length || 0;
20206     },
20207
20208     /**
20209      * Gets the total number of records in the dataset as returned by the server.
20210      * <p>
20211      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
20212      * the dataset size</em>
20213      */
20214     getTotalCount : function(){
20215         return this.totalLength || 0;
20216     },
20217
20218     /**
20219      * Returns the sort state of the Store as an object with two properties:
20220      * <pre><code>
20221  field {String} The name of the field by which the Records are sorted
20222  direction {String} The sort order, "ASC" or "DESC"
20223      * </code></pre>
20224      */
20225     getSortState : function(){
20226         return this.sortInfo;
20227     },
20228
20229     // private
20230     applySort : function(){
20231         if(this.sortInfo && !this.remoteSort){
20232             var s = this.sortInfo, f = s.field;
20233             var st = this.fields.get(f).sortType;
20234             var fn = function(r1, r2){
20235                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
20236                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
20237             };
20238             this.data.sort(s.direction, fn);
20239             if(this.snapshot && this.snapshot != this.data){
20240                 this.snapshot.sort(s.direction, fn);
20241             }
20242         }
20243     },
20244
20245     /**
20246      * Sets the default sort column and order to be used by the next load operation.
20247      * @param {String} fieldName The name of the field to sort by.
20248      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
20249      */
20250     setDefaultSort : function(field, dir){
20251         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
20252     },
20253
20254     /**
20255      * Sort the Records.
20256      * If remote sorting is used, the sort is performed on the server, and the cache is
20257      * reloaded. If local sorting is used, the cache is sorted internally.
20258      * @param {String} fieldName The name of the field to sort by.
20259      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
20260      */
20261     sort : function(fieldName, dir){
20262         var f = this.fields.get(fieldName);
20263         if(!dir){
20264             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
20265             
20266             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
20267                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
20268             }else{
20269                 dir = f.sortDir;
20270             }
20271         }
20272         this.sortToggle[f.name] = dir;
20273         this.sortInfo = {field: f.name, direction: dir};
20274         if(!this.remoteSort){
20275             this.applySort();
20276             this.fireEvent("datachanged", this);
20277         }else{
20278             this.load(this.lastOptions);
20279         }
20280     },
20281
20282     /**
20283      * Calls the specified function for each of the Records in the cache.
20284      * @param {Function} fn The function to call. The Record is passed as the first parameter.
20285      * Returning <em>false</em> aborts and exits the iteration.
20286      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
20287      */
20288     each : function(fn, scope){
20289         this.data.each(fn, scope);
20290     },
20291
20292     /**
20293      * Gets all records modified since the last commit.  Modified records are persisted across load operations
20294      * (e.g., during paging).
20295      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
20296      */
20297     getModifiedRecords : function(){
20298         return this.modified;
20299     },
20300
20301     // private
20302     createFilterFn : function(property, value, anyMatch){
20303         if(!value.exec){ // not a regex
20304             value = String(value);
20305             if(value.length == 0){
20306                 return false;
20307             }
20308             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
20309         }
20310         return function(r){
20311             return value.test(r.data[property]);
20312         };
20313     },
20314
20315     /**
20316      * Sums the value of <i>property</i> for each record between start and end and returns the result.
20317      * @param {String} property A field on your records
20318      * @param {Number} start The record index to start at (defaults to 0)
20319      * @param {Number} end The last record index to include (defaults to length - 1)
20320      * @return {Number} The sum
20321      */
20322     sum : function(property, start, end){
20323         var rs = this.data.items, v = 0;
20324         start = start || 0;
20325         end = (end || end === 0) ? end : rs.length-1;
20326
20327         for(var i = start; i <= end; i++){
20328             v += (rs[i].data[property] || 0);
20329         }
20330         return v;
20331     },
20332
20333     /**
20334      * Filter the records by a specified property.
20335      * @param {String} field A field on your records
20336      * @param {String/RegExp} value Either a string that the field
20337      * should start with or a RegExp to test against the field
20338      * @param {Boolean} anyMatch True to match any part not just the beginning
20339      */
20340     filter : function(property, value, anyMatch){
20341         var fn = this.createFilterFn(property, value, anyMatch);
20342         return fn ? this.filterBy(fn) : this.clearFilter();
20343     },
20344
20345     /**
20346      * Filter by a function. The specified function will be called with each
20347      * record in this data source. If the function returns true the record is included,
20348      * otherwise it is filtered.
20349      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
20350      * @param {Object} scope (optional) The scope of the function (defaults to this)
20351      */
20352     filterBy : function(fn, scope){
20353         this.snapshot = this.snapshot || this.data;
20354         this.data = this.queryBy(fn, scope||this);
20355         this.fireEvent("datachanged", this);
20356     },
20357
20358     /**
20359      * Query the records by a specified property.
20360      * @param {String} field A field on your records
20361      * @param {String/RegExp} value Either a string that the field
20362      * should start with or a RegExp to test against the field
20363      * @param {Boolean} anyMatch True to match any part not just the beginning
20364      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
20365      */
20366     query : function(property, value, anyMatch){
20367         var fn = this.createFilterFn(property, value, anyMatch);
20368         return fn ? this.queryBy(fn) : this.data.clone();
20369     },
20370
20371     /**
20372      * Query by a function. The specified function will be called with each
20373      * record in this data source. If the function returns true the record is included
20374      * in the results.
20375      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
20376      * @param {Object} scope (optional) The scope of the function (defaults to this)
20377       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
20378      **/
20379     queryBy : function(fn, scope){
20380         var data = this.snapshot || this.data;
20381         return data.filterBy(fn, scope||this);
20382     },
20383
20384     /**
20385      * Collects unique values for a particular dataIndex from this store.
20386      * @param {String} dataIndex The property to collect
20387      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
20388      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
20389      * @return {Array} An array of the unique values
20390      **/
20391     collect : function(dataIndex, allowNull, bypassFilter){
20392         var d = (bypassFilter === true && this.snapshot) ?
20393                 this.snapshot.items : this.data.items;
20394         var v, sv, r = [], l = {};
20395         for(var i = 0, len = d.length; i < len; i++){
20396             v = d[i].data[dataIndex];
20397             sv = String(v);
20398             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
20399                 l[sv] = true;
20400                 r[r.length] = v;
20401             }
20402         }
20403         return r;
20404     },
20405
20406     /**
20407      * Revert to a view of the Record cache with no filtering applied.
20408      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
20409      */
20410     clearFilter : function(suppressEvent){
20411         if(this.snapshot && this.snapshot != this.data){
20412             this.data = this.snapshot;
20413             delete this.snapshot;
20414             if(suppressEvent !== true){
20415                 this.fireEvent("datachanged", this);
20416             }
20417         }
20418     },
20419
20420     // private
20421     afterEdit : function(record){
20422         if(this.modified.indexOf(record) == -1){
20423             this.modified.push(record);
20424         }
20425         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
20426     },
20427     
20428     // private
20429     afterReject : function(record){
20430         this.modified.remove(record);
20431         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
20432     },
20433
20434     // private
20435     afterCommit : function(record){
20436         this.modified.remove(record);
20437         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
20438     },
20439
20440     /**
20441      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
20442      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
20443      */
20444     commitChanges : function(){
20445         var m = this.modified.slice(0);
20446         this.modified = [];
20447         for(var i = 0, len = m.length; i < len; i++){
20448             m[i].commit();
20449         }
20450     },
20451
20452     /**
20453      * Cancel outstanding changes on all changed records.
20454      */
20455     rejectChanges : function(){
20456         var m = this.modified.slice(0);
20457         this.modified = [];
20458         for(var i = 0, len = m.length; i < len; i++){
20459             m[i].reject();
20460         }
20461     },
20462
20463     onMetaChange : function(meta, rtype, o){
20464         this.recordType = rtype;
20465         this.fields = rtype.prototype.fields;
20466         delete this.snapshot;
20467         this.sortInfo = meta.sortInfo || this.sortInfo;
20468         this.modified = [];
20469         this.fireEvent('metachange', this, this.reader.meta);
20470     }
20471 });/*
20472  * Based on:
20473  * Ext JS Library 1.1.1
20474  * Copyright(c) 2006-2007, Ext JS, LLC.
20475  *
20476  * Originally Released Under LGPL - original licence link has changed is not relivant.
20477  *
20478  * Fork - LGPL
20479  * <script type="text/javascript">
20480  */
20481
20482 /**
20483  * @class Roo.data.SimpleStore
20484  * @extends Roo.data.Store
20485  * Small helper class to make creating Stores from Array data easier.
20486  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
20487  * @cfg {Array} fields An array of field definition objects, or field name strings.
20488  * @cfg {Array} data The multi-dimensional array of data
20489  * @constructor
20490  * @param {Object} config
20491  */
20492 Roo.data.SimpleStore = function(config){
20493     Roo.data.SimpleStore.superclass.constructor.call(this, {
20494         isLocal : true,
20495         reader: new Roo.data.ArrayReader({
20496                 id: config.id
20497             },
20498             Roo.data.Record.create(config.fields)
20499         ),
20500         proxy : new Roo.data.MemoryProxy(config.data)
20501     });
20502     this.load();
20503 };
20504 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
20505  * Based on:
20506  * Ext JS Library 1.1.1
20507  * Copyright(c) 2006-2007, Ext JS, LLC.
20508  *
20509  * Originally Released Under LGPL - original licence link has changed is not relivant.
20510  *
20511  * Fork - LGPL
20512  * <script type="text/javascript">
20513  */
20514
20515 /**
20516 /**
20517  * @extends Roo.data.Store
20518  * @class Roo.data.JsonStore
20519  * Small helper class to make creating Stores for JSON data easier. <br/>
20520 <pre><code>
20521 var store = new Roo.data.JsonStore({
20522     url: 'get-images.php',
20523     root: 'images',
20524     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
20525 });
20526 </code></pre>
20527  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
20528  * JsonReader and HttpProxy (unless inline data is provided).</b>
20529  * @cfg {Array} fields An array of field definition objects, or field name strings.
20530  * @constructor
20531  * @param {Object} config
20532  */
20533 Roo.data.JsonStore = function(c){
20534     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
20535         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
20536         reader: new Roo.data.JsonReader(c, c.fields)
20537     }));
20538 };
20539 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
20540  * Based on:
20541  * Ext JS Library 1.1.1
20542  * Copyright(c) 2006-2007, Ext JS, LLC.
20543  *
20544  * Originally Released Under LGPL - original licence link has changed is not relivant.
20545  *
20546  * Fork - LGPL
20547  * <script type="text/javascript">
20548  */
20549
20550  
20551 Roo.data.Field = function(config){
20552     if(typeof config == "string"){
20553         config = {name: config};
20554     }
20555     Roo.apply(this, config);
20556     
20557     if(!this.type){
20558         this.type = "auto";
20559     }
20560     
20561     var st = Roo.data.SortTypes;
20562     // named sortTypes are supported, here we look them up
20563     if(typeof this.sortType == "string"){
20564         this.sortType = st[this.sortType];
20565     }
20566     
20567     // set default sortType for strings and dates
20568     if(!this.sortType){
20569         switch(this.type){
20570             case "string":
20571                 this.sortType = st.asUCString;
20572                 break;
20573             case "date":
20574                 this.sortType = st.asDate;
20575                 break;
20576             default:
20577                 this.sortType = st.none;
20578         }
20579     }
20580
20581     // define once
20582     var stripRe = /[\$,%]/g;
20583
20584     // prebuilt conversion function for this field, instead of
20585     // switching every time we're reading a value
20586     if(!this.convert){
20587         var cv, dateFormat = this.dateFormat;
20588         switch(this.type){
20589             case "":
20590             case "auto":
20591             case undefined:
20592                 cv = function(v){ return v; };
20593                 break;
20594             case "string":
20595                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20596                 break;
20597             case "int":
20598                 cv = function(v){
20599                     return v !== undefined && v !== null && v !== '' ?
20600                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20601                     };
20602                 break;
20603             case "float":
20604                 cv = function(v){
20605                     return v !== undefined && v !== null && v !== '' ?
20606                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20607                     };
20608                 break;
20609             case "bool":
20610             case "boolean":
20611                 cv = function(v){ return v === true || v === "true" || v == 1; };
20612                 break;
20613             case "date":
20614                 cv = function(v){
20615                     if(!v){
20616                         return '';
20617                     }
20618                     if(v instanceof Date){
20619                         return v;
20620                     }
20621                     if(dateFormat){
20622                         if(dateFormat == "timestamp"){
20623                             return new Date(v*1000);
20624                         }
20625                         return Date.parseDate(v, dateFormat);
20626                     }
20627                     var parsed = Date.parse(v);
20628                     return parsed ? new Date(parsed) : null;
20629                 };
20630              break;
20631             
20632         }
20633         this.convert = cv;
20634     }
20635 };
20636
20637 Roo.data.Field.prototype = {
20638     dateFormat: null,
20639     defaultValue: "",
20640     mapping: null,
20641     sortType : null,
20642     sortDir : "ASC"
20643 };/*
20644  * Based on:
20645  * Ext JS Library 1.1.1
20646  * Copyright(c) 2006-2007, Ext JS, LLC.
20647  *
20648  * Originally Released Under LGPL - original licence link has changed is not relivant.
20649  *
20650  * Fork - LGPL
20651  * <script type="text/javascript">
20652  */
20653  
20654 // Base class for reading structured data from a data source.  This class is intended to be
20655 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20656
20657 /**
20658  * @class Roo.data.DataReader
20659  * Base class for reading structured data from a data source.  This class is intended to be
20660  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20661  */
20662
20663 Roo.data.DataReader = function(meta, recordType){
20664     
20665     this.meta = meta;
20666     
20667     this.recordType = recordType instanceof Array ? 
20668         Roo.data.Record.create(recordType) : recordType;
20669 };
20670
20671 Roo.data.DataReader.prototype = {
20672      /**
20673      * Create an empty record
20674      * @param {Object} data (optional) - overlay some values
20675      * @return {Roo.data.Record} record created.
20676      */
20677     newRow :  function(d) {
20678         var da =  {};
20679         this.recordType.prototype.fields.each(function(c) {
20680             switch( c.type) {
20681                 case 'int' : da[c.name] = 0; break;
20682                 case 'date' : da[c.name] = new Date(); break;
20683                 case 'float' : da[c.name] = 0.0; break;
20684                 case 'boolean' : da[c.name] = false; break;
20685                 default : da[c.name] = ""; break;
20686             }
20687             
20688         });
20689         return new this.recordType(Roo.apply(da, d));
20690     }
20691     
20692 };/*
20693  * Based on:
20694  * Ext JS Library 1.1.1
20695  * Copyright(c) 2006-2007, Ext JS, LLC.
20696  *
20697  * Originally Released Under LGPL - original licence link has changed is not relivant.
20698  *
20699  * Fork - LGPL
20700  * <script type="text/javascript">
20701  */
20702
20703 /**
20704  * @class Roo.data.DataProxy
20705  * @extends Roo.data.Observable
20706  * This class is an abstract base class for implementations which provide retrieval of
20707  * unformatted data objects.<br>
20708  * <p>
20709  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20710  * (of the appropriate type which knows how to parse the data object) to provide a block of
20711  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20712  * <p>
20713  * Custom implementations must implement the load method as described in
20714  * {@link Roo.data.HttpProxy#load}.
20715  */
20716 Roo.data.DataProxy = function(){
20717     this.addEvents({
20718         /**
20719          * @event beforeload
20720          * Fires before a network request is made to retrieve a data object.
20721          * @param {Object} This DataProxy object.
20722          * @param {Object} params The params parameter to the load function.
20723          */
20724         beforeload : true,
20725         /**
20726          * @event load
20727          * Fires before the load method's callback is called.
20728          * @param {Object} This DataProxy object.
20729          * @param {Object} o The data object.
20730          * @param {Object} arg The callback argument object passed to the load function.
20731          */
20732         load : true,
20733         /**
20734          * @event loadexception
20735          * Fires if an Exception occurs during data retrieval.
20736          * @param {Object} This DataProxy object.
20737          * @param {Object} o The data object.
20738          * @param {Object} arg The callback argument object passed to the load function.
20739          * @param {Object} e The Exception.
20740          */
20741         loadexception : true
20742     });
20743     Roo.data.DataProxy.superclass.constructor.call(this);
20744 };
20745
20746 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20747
20748     /**
20749      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20750      */
20751 /*
20752  * Based on:
20753  * Ext JS Library 1.1.1
20754  * Copyright(c) 2006-2007, Ext JS, LLC.
20755  *
20756  * Originally Released Under LGPL - original licence link has changed is not relivant.
20757  *
20758  * Fork - LGPL
20759  * <script type="text/javascript">
20760  */
20761 /**
20762  * @class Roo.data.MemoryProxy
20763  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20764  * to the Reader when its load method is called.
20765  * @constructor
20766  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20767  */
20768 Roo.data.MemoryProxy = function(data){
20769     if (data.data) {
20770         data = data.data;
20771     }
20772     Roo.data.MemoryProxy.superclass.constructor.call(this);
20773     this.data = data;
20774 };
20775
20776 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20777     /**
20778      * Load data from the requested source (in this case an in-memory
20779      * data object passed to the constructor), read the data object into
20780      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20781      * process that block using the passed callback.
20782      * @param {Object} params This parameter is not used by the MemoryProxy class.
20783      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20784      * object into a block of Roo.data.Records.
20785      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20786      * The function must be passed <ul>
20787      * <li>The Record block object</li>
20788      * <li>The "arg" argument from the load function</li>
20789      * <li>A boolean success indicator</li>
20790      * </ul>
20791      * @param {Object} scope The scope in which to call the callback
20792      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20793      */
20794     load : function(params, reader, callback, scope, arg){
20795         params = params || {};
20796         var result;
20797         try {
20798             result = reader.readRecords(this.data);
20799         }catch(e){
20800             this.fireEvent("loadexception", this, arg, null, e);
20801             callback.call(scope, null, arg, false);
20802             return;
20803         }
20804         callback.call(scope, result, arg, true);
20805     },
20806     
20807     // private
20808     update : function(params, records){
20809         
20810     }
20811 });/*
20812  * Based on:
20813  * Ext JS Library 1.1.1
20814  * Copyright(c) 2006-2007, Ext JS, LLC.
20815  *
20816  * Originally Released Under LGPL - original licence link has changed is not relivant.
20817  *
20818  * Fork - LGPL
20819  * <script type="text/javascript">
20820  */
20821 /**
20822  * @class Roo.data.HttpProxy
20823  * @extends Roo.data.DataProxy
20824  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20825  * configured to reference a certain URL.<br><br>
20826  * <p>
20827  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20828  * from which the running page was served.<br><br>
20829  * <p>
20830  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20831  * <p>
20832  * Be aware that to enable the browser to parse an XML document, the server must set
20833  * the Content-Type header in the HTTP response to "text/xml".
20834  * @constructor
20835  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20836  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20837  * will be used to make the request.
20838  */
20839 Roo.data.HttpProxy = function(conn){
20840     Roo.data.HttpProxy.superclass.constructor.call(this);
20841     // is conn a conn config or a real conn?
20842     this.conn = conn;
20843     this.useAjax = !conn || !conn.events;
20844   
20845 };
20846
20847 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20848     // thse are take from connection...
20849     
20850     /**
20851      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20852      */
20853     /**
20854      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20855      * extra parameters to each request made by this object. (defaults to undefined)
20856      */
20857     /**
20858      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20859      *  to each request made by this object. (defaults to undefined)
20860      */
20861     /**
20862      * @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)
20863      */
20864     /**
20865      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20866      */
20867      /**
20868      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20869      * @type Boolean
20870      */
20871   
20872
20873     /**
20874      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20875      * @type Boolean
20876      */
20877     /**
20878      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20879      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20880      * a finer-grained basis than the DataProxy events.
20881      */
20882     getConnection : function(){
20883         return this.useAjax ? Roo.Ajax : this.conn;
20884     },
20885
20886     /**
20887      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20888      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20889      * process that block using the passed callback.
20890      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20891      * for the request to the remote server.
20892      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20893      * object into a block of Roo.data.Records.
20894      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20895      * The function must be passed <ul>
20896      * <li>The Record block object</li>
20897      * <li>The "arg" argument from the load function</li>
20898      * <li>A boolean success indicator</li>
20899      * </ul>
20900      * @param {Object} scope The scope in which to call the callback
20901      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20902      */
20903     load : function(params, reader, callback, scope, arg){
20904         if(this.fireEvent("beforeload", this, params) !== false){
20905             var  o = {
20906                 params : params || {},
20907                 request: {
20908                     callback : callback,
20909                     scope : scope,
20910                     arg : arg
20911                 },
20912                 reader: reader,
20913                 callback : this.loadResponse,
20914                 scope: this
20915             };
20916             if(this.useAjax){
20917                 Roo.applyIf(o, this.conn);
20918                 if(this.activeRequest){
20919                     Roo.Ajax.abort(this.activeRequest);
20920                 }
20921                 this.activeRequest = Roo.Ajax.request(o);
20922             }else{
20923                 this.conn.request(o);
20924             }
20925         }else{
20926             callback.call(scope||this, null, arg, false);
20927         }
20928     },
20929
20930     // private
20931     loadResponse : function(o, success, response){
20932         delete this.activeRequest;
20933         if(!success){
20934             this.fireEvent("loadexception", this, o, response);
20935             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20936             return;
20937         }
20938         var result;
20939         try {
20940             result = o.reader.read(response);
20941         }catch(e){
20942             this.fireEvent("loadexception", this, o, response, e);
20943             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20944             return;
20945         }
20946         
20947         this.fireEvent("load", this, o, o.request.arg);
20948         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20949     },
20950
20951     // private
20952     update : function(dataSet){
20953
20954     },
20955
20956     // private
20957     updateResponse : function(dataSet){
20958
20959     }
20960 });/*
20961  * Based on:
20962  * Ext JS Library 1.1.1
20963  * Copyright(c) 2006-2007, Ext JS, LLC.
20964  *
20965  * Originally Released Under LGPL - original licence link has changed is not relivant.
20966  *
20967  * Fork - LGPL
20968  * <script type="text/javascript">
20969  */
20970
20971 /**
20972  * @class Roo.data.ScriptTagProxy
20973  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20974  * other than the originating domain of the running page.<br><br>
20975  * <p>
20976  * <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
20977  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20978  * <p>
20979  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20980  * source code that is used as the source inside a &lt;script> tag.<br><br>
20981  * <p>
20982  * In order for the browser to process the returned data, the server must wrap the data object
20983  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20984  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20985  * depending on whether the callback name was passed:
20986  * <p>
20987  * <pre><code>
20988 boolean scriptTag = false;
20989 String cb = request.getParameter("callback");
20990 if (cb != null) {
20991     scriptTag = true;
20992     response.setContentType("text/javascript");
20993 } else {
20994     response.setContentType("application/x-json");
20995 }
20996 Writer out = response.getWriter();
20997 if (scriptTag) {
20998     out.write(cb + "(");
20999 }
21000 out.print(dataBlock.toJsonString());
21001 if (scriptTag) {
21002     out.write(");");
21003 }
21004 </pre></code>
21005  *
21006  * @constructor
21007  * @param {Object} config A configuration object.
21008  */
21009 Roo.data.ScriptTagProxy = function(config){
21010     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
21011     Roo.apply(this, config);
21012     this.head = document.getElementsByTagName("head")[0];
21013 };
21014
21015 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
21016
21017 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
21018     /**
21019      * @cfg {String} url The URL from which to request the data object.
21020      */
21021     /**
21022      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
21023      */
21024     timeout : 30000,
21025     /**
21026      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
21027      * the server the name of the callback function set up by the load call to process the returned data object.
21028      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
21029      * javascript output which calls this named function passing the data object as its only parameter.
21030      */
21031     callbackParam : "callback",
21032     /**
21033      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
21034      * name to the request.
21035      */
21036     nocache : true,
21037
21038     /**
21039      * Load data from the configured URL, read the data object into
21040      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
21041      * process that block using the passed callback.
21042      * @param {Object} params An object containing properties which are to be used as HTTP parameters
21043      * for the request to the remote server.
21044      * @param {Roo.data.DataReader} reader The Reader object which converts the data
21045      * object into a block of Roo.data.Records.
21046      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
21047      * The function must be passed <ul>
21048      * <li>The Record block object</li>
21049      * <li>The "arg" argument from the load function</li>
21050      * <li>A boolean success indicator</li>
21051      * </ul>
21052      * @param {Object} scope The scope in which to call the callback
21053      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
21054      */
21055     load : function(params, reader, callback, scope, arg){
21056         if(this.fireEvent("beforeload", this, params) !== false){
21057
21058             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
21059
21060             var url = this.url;
21061             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
21062             if(this.nocache){
21063                 url += "&_dc=" + (new Date().getTime());
21064             }
21065             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
21066             var trans = {
21067                 id : transId,
21068                 cb : "stcCallback"+transId,
21069                 scriptId : "stcScript"+transId,
21070                 params : params,
21071                 arg : arg,
21072                 url : url,
21073                 callback : callback,
21074                 scope : scope,
21075                 reader : reader
21076             };
21077             var conn = this;
21078
21079             window[trans.cb] = function(o){
21080                 conn.handleResponse(o, trans);
21081             };
21082
21083             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
21084
21085             if(this.autoAbort !== false){
21086                 this.abort();
21087             }
21088
21089             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
21090
21091             var script = document.createElement("script");
21092             script.setAttribute("src", url);
21093             script.setAttribute("type", "text/javascript");
21094             script.setAttribute("id", trans.scriptId);
21095             this.head.appendChild(script);
21096
21097             this.trans = trans;
21098         }else{
21099             callback.call(scope||this, null, arg, false);
21100         }
21101     },
21102
21103     // private
21104     isLoading : function(){
21105         return this.trans ? true : false;
21106     },
21107
21108     /**
21109      * Abort the current server request.
21110      */
21111     abort : function(){
21112         if(this.isLoading()){
21113             this.destroyTrans(this.trans);
21114         }
21115     },
21116
21117     // private
21118     destroyTrans : function(trans, isLoaded){
21119         this.head.removeChild(document.getElementById(trans.scriptId));
21120         clearTimeout(trans.timeoutId);
21121         if(isLoaded){
21122             window[trans.cb] = undefined;
21123             try{
21124                 delete window[trans.cb];
21125             }catch(e){}
21126         }else{
21127             // if hasn't been loaded, wait for load to remove it to prevent script error
21128             window[trans.cb] = function(){
21129                 window[trans.cb] = undefined;
21130                 try{
21131                     delete window[trans.cb];
21132                 }catch(e){}
21133             };
21134         }
21135     },
21136
21137     // private
21138     handleResponse : function(o, trans){
21139         this.trans = false;
21140         this.destroyTrans(trans, true);
21141         var result;
21142         try {
21143             result = trans.reader.readRecords(o);
21144         }catch(e){
21145             this.fireEvent("loadexception", this, o, trans.arg, e);
21146             trans.callback.call(trans.scope||window, null, trans.arg, false);
21147             return;
21148         }
21149         this.fireEvent("load", this, o, trans.arg);
21150         trans.callback.call(trans.scope||window, result, trans.arg, true);
21151     },
21152
21153     // private
21154     handleFailure : function(trans){
21155         this.trans = false;
21156         this.destroyTrans(trans, false);
21157         this.fireEvent("loadexception", this, null, trans.arg);
21158         trans.callback.call(trans.scope||window, null, trans.arg, false);
21159     }
21160 });/*
21161  * Based on:
21162  * Ext JS Library 1.1.1
21163  * Copyright(c) 2006-2007, Ext JS, LLC.
21164  *
21165  * Originally Released Under LGPL - original licence link has changed is not relivant.
21166  *
21167  * Fork - LGPL
21168  * <script type="text/javascript">
21169  */
21170
21171 /**
21172  * @class Roo.data.JsonReader
21173  * @extends Roo.data.DataReader
21174  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
21175  * based on mappings in a provided Roo.data.Record constructor.
21176  * 
21177  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
21178  * in the reply previously. 
21179  * 
21180  * <p>
21181  * Example code:
21182  * <pre><code>
21183 var RecordDef = Roo.data.Record.create([
21184     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
21185     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
21186 ]);
21187 var myReader = new Roo.data.JsonReader({
21188     totalProperty: "results",    // The property which contains the total dataset size (optional)
21189     root: "rows",                // The property which contains an Array of row objects
21190     id: "id"                     // The property within each row object that provides an ID for the record (optional)
21191 }, RecordDef);
21192 </code></pre>
21193  * <p>
21194  * This would consume a JSON file like this:
21195  * <pre><code>
21196 { 'results': 2, 'rows': [
21197     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
21198     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
21199 }
21200 </code></pre>
21201  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
21202  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
21203  * paged from the remote server.
21204  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
21205  * @cfg {String} root name of the property which contains the Array of row objects.
21206  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
21207  * @constructor
21208  * Create a new JsonReader
21209  * @param {Object} meta Metadata configuration options
21210  * @param {Object} recordType Either an Array of field definition objects,
21211  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
21212  */
21213 Roo.data.JsonReader = function(meta, recordType){
21214     
21215     meta = meta || {};
21216     // set some defaults:
21217     Roo.applyIf(meta, {
21218         totalProperty: 'total',
21219         successProperty : 'success',
21220         root : 'data',
21221         id : 'id'
21222     });
21223     
21224     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
21225 };
21226 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
21227     
21228     /**
21229      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
21230      * Used by Store query builder to append _requestMeta to params.
21231      * 
21232      */
21233     metaFromRemote : false,
21234     /**
21235      * This method is only used by a DataProxy which has retrieved data from a remote server.
21236      * @param {Object} response The XHR object which contains the JSON data in its responseText.
21237      * @return {Object} data A data block which is used by an Roo.data.Store object as
21238      * a cache of Roo.data.Records.
21239      */
21240     read : function(response){
21241         var json = response.responseText;
21242        
21243         var o = /* eval:var:o */ eval("("+json+")");
21244         if(!o) {
21245             throw {message: "JsonReader.read: Json object not found"};
21246         }
21247         
21248         if(o.metaData){
21249             
21250             delete this.ef;
21251             this.metaFromRemote = true;
21252             this.meta = o.metaData;
21253             this.recordType = Roo.data.Record.create(o.metaData.fields);
21254             this.onMetaChange(this.meta, this.recordType, o);
21255         }
21256         return this.readRecords(o);
21257     },
21258
21259     // private function a store will implement
21260     onMetaChange : function(meta, recordType, o){
21261
21262     },
21263
21264     /**
21265          * @ignore
21266          */
21267     simpleAccess: function(obj, subsc) {
21268         return obj[subsc];
21269     },
21270
21271         /**
21272          * @ignore
21273          */
21274     getJsonAccessor: function(){
21275         var re = /[\[\.]/;
21276         return function(expr) {
21277             try {
21278                 return(re.test(expr))
21279                     ? new Function("obj", "return obj." + expr)
21280                     : function(obj){
21281                         return obj[expr];
21282                     };
21283             } catch(e){}
21284             return Roo.emptyFn;
21285         };
21286     }(),
21287
21288     /**
21289      * Create a data block containing Roo.data.Records from an XML document.
21290      * @param {Object} o An object which contains an Array of row objects in the property specified
21291      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
21292      * which contains the total size of the dataset.
21293      * @return {Object} data A data block which is used by an Roo.data.Store object as
21294      * a cache of Roo.data.Records.
21295      */
21296     readRecords : function(o){
21297         /**
21298          * After any data loads, the raw JSON data is available for further custom processing.
21299          * @type Object
21300          */
21301         this.o = o;
21302         var s = this.meta, Record = this.recordType,
21303             f = Record.prototype.fields, fi = f.items, fl = f.length;
21304
21305 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
21306         if (!this.ef) {
21307             if(s.totalProperty) {
21308                     this.getTotal = this.getJsonAccessor(s.totalProperty);
21309                 }
21310                 if(s.successProperty) {
21311                     this.getSuccess = this.getJsonAccessor(s.successProperty);
21312                 }
21313                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
21314                 if (s.id) {
21315                         var g = this.getJsonAccessor(s.id);
21316                         this.getId = function(rec) {
21317                                 var r = g(rec);
21318                                 return (r === undefined || r === "") ? null : r;
21319                         };
21320                 } else {
21321                         this.getId = function(){return null;};
21322                 }
21323             this.ef = [];
21324             for(var jj = 0; jj < fl; jj++){
21325                 f = fi[jj];
21326                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
21327                 this.ef[jj] = this.getJsonAccessor(map);
21328             }
21329         }
21330
21331         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
21332         if(s.totalProperty){
21333             var vt = parseInt(this.getTotal(o), 10);
21334             if(!isNaN(vt)){
21335                 totalRecords = vt;
21336             }
21337         }
21338         if(s.successProperty){
21339             var vs = this.getSuccess(o);
21340             if(vs === false || vs === 'false'){
21341                 success = false;
21342             }
21343         }
21344         var records = [];
21345             for(var i = 0; i < c; i++){
21346                     var n = root[i];
21347                 var values = {};
21348                 var id = this.getId(n);
21349                 for(var j = 0; j < fl; j++){
21350                     f = fi[j];
21351                 var v = this.ef[j](n);
21352                 if (!f.convert) {
21353                     Roo.log('missing convert for ' + f.name);
21354                     Roo.log(f);
21355                     continue;
21356                 }
21357                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
21358                 }
21359                 var record = new Record(values, id);
21360                 record.json = n;
21361                 records[i] = record;
21362             }
21363             return {
21364             raw : o,
21365                 success : success,
21366                 records : records,
21367                 totalRecords : totalRecords
21368             };
21369     }
21370 });/*
21371  * Based on:
21372  * Ext JS Library 1.1.1
21373  * Copyright(c) 2006-2007, Ext JS, LLC.
21374  *
21375  * Originally Released Under LGPL - original licence link has changed is not relivant.
21376  *
21377  * Fork - LGPL
21378  * <script type="text/javascript">
21379  */
21380
21381 /**
21382  * @class Roo.data.XmlReader
21383  * @extends Roo.data.DataReader
21384  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
21385  * based on mappings in a provided Roo.data.Record constructor.<br><br>
21386  * <p>
21387  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
21388  * header in the HTTP response must be set to "text/xml".</em>
21389  * <p>
21390  * Example code:
21391  * <pre><code>
21392 var RecordDef = Roo.data.Record.create([
21393    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
21394    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
21395 ]);
21396 var myReader = new Roo.data.XmlReader({
21397    totalRecords: "results", // The element which contains the total dataset size (optional)
21398    record: "row",           // The repeated element which contains row information
21399    id: "id"                 // The element within the row that provides an ID for the record (optional)
21400 }, RecordDef);
21401 </code></pre>
21402  * <p>
21403  * This would consume an XML file like this:
21404  * <pre><code>
21405 &lt;?xml?>
21406 &lt;dataset>
21407  &lt;results>2&lt;/results>
21408  &lt;row>
21409    &lt;id>1&lt;/id>
21410    &lt;name>Bill&lt;/name>
21411    &lt;occupation>Gardener&lt;/occupation>
21412  &lt;/row>
21413  &lt;row>
21414    &lt;id>2&lt;/id>
21415    &lt;name>Ben&lt;/name>
21416    &lt;occupation>Horticulturalist&lt;/occupation>
21417  &lt;/row>
21418 &lt;/dataset>
21419 </code></pre>
21420  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
21421  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
21422  * paged from the remote server.
21423  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
21424  * @cfg {String} success The DomQuery path to the success attribute used by forms.
21425  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
21426  * a record identifier value.
21427  * @constructor
21428  * Create a new XmlReader
21429  * @param {Object} meta Metadata configuration options
21430  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
21431  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
21432  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
21433  */
21434 Roo.data.XmlReader = function(meta, recordType){
21435     meta = meta || {};
21436     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
21437 };
21438 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
21439     /**
21440      * This method is only used by a DataProxy which has retrieved data from a remote server.
21441          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
21442          * to contain a method called 'responseXML' that returns an XML document object.
21443      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
21444      * a cache of Roo.data.Records.
21445      */
21446     read : function(response){
21447         var doc = response.responseXML;
21448         if(!doc) {
21449             throw {message: "XmlReader.read: XML Document not available"};
21450         }
21451         return this.readRecords(doc);
21452     },
21453
21454     /**
21455      * Create a data block containing Roo.data.Records from an XML document.
21456          * @param {Object} doc A parsed XML document.
21457      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
21458      * a cache of Roo.data.Records.
21459      */
21460     readRecords : function(doc){
21461         /**
21462          * After any data loads/reads, the raw XML Document is available for further custom processing.
21463          * @type XMLDocument
21464          */
21465         this.xmlData = doc;
21466         var root = doc.documentElement || doc;
21467         var q = Roo.DomQuery;
21468         var recordType = this.recordType, fields = recordType.prototype.fields;
21469         var sid = this.meta.id;
21470         var totalRecords = 0, success = true;
21471         if(this.meta.totalRecords){
21472             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
21473         }
21474         
21475         if(this.meta.success){
21476             var sv = q.selectValue(this.meta.success, root, true);
21477             success = sv !== false && sv !== 'false';
21478         }
21479         var records = [];
21480         var ns = q.select(this.meta.record, root);
21481         for(var i = 0, len = ns.length; i < len; i++) {
21482                 var n = ns[i];
21483                 var values = {};
21484                 var id = sid ? q.selectValue(sid, n) : undefined;
21485                 for(var j = 0, jlen = fields.length; j < jlen; j++){
21486                     var f = fields.items[j];
21487                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
21488                     v = f.convert(v);
21489                     values[f.name] = v;
21490                 }
21491                 var record = new recordType(values, id);
21492                 record.node = n;
21493                 records[records.length] = record;
21494             }
21495
21496             return {
21497                 success : success,
21498                 records : records,
21499                 totalRecords : totalRecords || records.length
21500             };
21501     }
21502 });/*
21503  * Based on:
21504  * Ext JS Library 1.1.1
21505  * Copyright(c) 2006-2007, Ext JS, LLC.
21506  *
21507  * Originally Released Under LGPL - original licence link has changed is not relivant.
21508  *
21509  * Fork - LGPL
21510  * <script type="text/javascript">
21511  */
21512
21513 /**
21514  * @class Roo.data.ArrayReader
21515  * @extends Roo.data.DataReader
21516  * Data reader class to create an Array of Roo.data.Record objects from an Array.
21517  * Each element of that Array represents a row of data fields. The
21518  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
21519  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
21520  * <p>
21521  * Example code:.
21522  * <pre><code>
21523 var RecordDef = Roo.data.Record.create([
21524     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
21525     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
21526 ]);
21527 var myReader = new Roo.data.ArrayReader({
21528     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
21529 }, RecordDef);
21530 </code></pre>
21531  * <p>
21532  * This would consume an Array like this:
21533  * <pre><code>
21534 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
21535   </code></pre>
21536  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
21537  * @constructor
21538  * Create a new JsonReader
21539  * @param {Object} meta Metadata configuration options.
21540  * @param {Object} recordType Either an Array of field definition objects
21541  * as specified to {@link Roo.data.Record#create},
21542  * or an {@link Roo.data.Record} object
21543  * created using {@link Roo.data.Record#create}.
21544  */
21545 Roo.data.ArrayReader = function(meta, recordType){
21546     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
21547 };
21548
21549 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
21550     /**
21551      * Create a data block containing Roo.data.Records from an XML document.
21552      * @param {Object} o An Array of row objects which represents the dataset.
21553      * @return {Object} data A data block which is used by an Roo.data.Store object as
21554      * a cache of Roo.data.Records.
21555      */
21556     readRecords : function(o){
21557         var sid = this.meta ? this.meta.id : null;
21558         var recordType = this.recordType, fields = recordType.prototype.fields;
21559         var records = [];
21560         var root = o;
21561             for(var i = 0; i < root.length; i++){
21562                     var n = root[i];
21563                 var values = {};
21564                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
21565                 for(var j = 0, jlen = fields.length; j < jlen; j++){
21566                 var f = fields.items[j];
21567                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
21568                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
21569                 v = f.convert(v);
21570                 values[f.name] = v;
21571             }
21572                 var record = new recordType(values, id);
21573                 record.json = n;
21574                 records[records.length] = record;
21575             }
21576             return {
21577                 records : records,
21578                 totalRecords : records.length
21579             };
21580     }
21581 });/*
21582  * Based on:
21583  * Ext JS Library 1.1.1
21584  * Copyright(c) 2006-2007, Ext JS, LLC.
21585  *
21586  * Originally Released Under LGPL - original licence link has changed is not relivant.
21587  *
21588  * Fork - LGPL
21589  * <script type="text/javascript">
21590  */
21591
21592
21593 /**
21594  * @class Roo.data.Tree
21595  * @extends Roo.util.Observable
21596  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21597  * in the tree have most standard DOM functionality.
21598  * @constructor
21599  * @param {Node} root (optional) The root node
21600  */
21601 Roo.data.Tree = function(root){
21602    this.nodeHash = {};
21603    /**
21604     * The root node for this tree
21605     * @type Node
21606     */
21607    this.root = null;
21608    if(root){
21609        this.setRootNode(root);
21610    }
21611    this.addEvents({
21612        /**
21613         * @event append
21614         * Fires when a new child node is appended to a node in this tree.
21615         * @param {Tree} tree The owner tree
21616         * @param {Node} parent The parent node
21617         * @param {Node} node The newly appended node
21618         * @param {Number} index The index of the newly appended node
21619         */
21620        "append" : true,
21621        /**
21622         * @event remove
21623         * Fires when a child node is removed from a node in this tree.
21624         * @param {Tree} tree The owner tree
21625         * @param {Node} parent The parent node
21626         * @param {Node} node The child node removed
21627         */
21628        "remove" : true,
21629        /**
21630         * @event move
21631         * Fires when a node is moved to a new location in the tree
21632         * @param {Tree} tree The owner tree
21633         * @param {Node} node The node moved
21634         * @param {Node} oldParent The old parent of this node
21635         * @param {Node} newParent The new parent of this node
21636         * @param {Number} index The index it was moved to
21637         */
21638        "move" : true,
21639        /**
21640         * @event insert
21641         * Fires when a new child node is inserted in a node in this tree.
21642         * @param {Tree} tree The owner tree
21643         * @param {Node} parent The parent node
21644         * @param {Node} node The child node inserted
21645         * @param {Node} refNode The child node the node was inserted before
21646         */
21647        "insert" : true,
21648        /**
21649         * @event beforeappend
21650         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21651         * @param {Tree} tree The owner tree
21652         * @param {Node} parent The parent node
21653         * @param {Node} node The child node to be appended
21654         */
21655        "beforeappend" : true,
21656        /**
21657         * @event beforeremove
21658         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21659         * @param {Tree} tree The owner tree
21660         * @param {Node} parent The parent node
21661         * @param {Node} node The child node to be removed
21662         */
21663        "beforeremove" : true,
21664        /**
21665         * @event beforemove
21666         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21667         * @param {Tree} tree The owner tree
21668         * @param {Node} node The node being moved
21669         * @param {Node} oldParent The parent of the node
21670         * @param {Node} newParent The new parent the node is moving to
21671         * @param {Number} index The index it is being moved to
21672         */
21673        "beforemove" : true,
21674        /**
21675         * @event beforeinsert
21676         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21677         * @param {Tree} tree The owner tree
21678         * @param {Node} parent The parent node
21679         * @param {Node} node The child node to be inserted
21680         * @param {Node} refNode The child node the node is being inserted before
21681         */
21682        "beforeinsert" : true
21683    });
21684
21685     Roo.data.Tree.superclass.constructor.call(this);
21686 };
21687
21688 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21689     pathSeparator: "/",
21690
21691     proxyNodeEvent : function(){
21692         return this.fireEvent.apply(this, arguments);
21693     },
21694
21695     /**
21696      * Returns the root node for this tree.
21697      * @return {Node}
21698      */
21699     getRootNode : function(){
21700         return this.root;
21701     },
21702
21703     /**
21704      * Sets the root node for this tree.
21705      * @param {Node} node
21706      * @return {Node}
21707      */
21708     setRootNode : function(node){
21709         this.root = node;
21710         node.ownerTree = this;
21711         node.isRoot = true;
21712         this.registerNode(node);
21713         return node;
21714     },
21715
21716     /**
21717      * Gets a node in this tree by its id.
21718      * @param {String} id
21719      * @return {Node}
21720      */
21721     getNodeById : function(id){
21722         return this.nodeHash[id];
21723     },
21724
21725     registerNode : function(node){
21726         this.nodeHash[node.id] = node;
21727     },
21728
21729     unregisterNode : function(node){
21730         delete this.nodeHash[node.id];
21731     },
21732
21733     toString : function(){
21734         return "[Tree"+(this.id?" "+this.id:"")+"]";
21735     }
21736 });
21737
21738 /**
21739  * @class Roo.data.Node
21740  * @extends Roo.util.Observable
21741  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21742  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21743  * @constructor
21744  * @param {Object} attributes The attributes/config for the node
21745  */
21746 Roo.data.Node = function(attributes){
21747     /**
21748      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21749      * @type {Object}
21750      */
21751     this.attributes = attributes || {};
21752     this.leaf = this.attributes.leaf;
21753     /**
21754      * The node id. @type String
21755      */
21756     this.id = this.attributes.id;
21757     if(!this.id){
21758         this.id = Roo.id(null, "ynode-");
21759         this.attributes.id = this.id;
21760     }
21761      
21762     
21763     /**
21764      * All child nodes of this node. @type Array
21765      */
21766     this.childNodes = [];
21767     if(!this.childNodes.indexOf){ // indexOf is a must
21768         this.childNodes.indexOf = function(o){
21769             for(var i = 0, len = this.length; i < len; i++){
21770                 if(this[i] == o) {
21771                     return i;
21772                 }
21773             }
21774             return -1;
21775         };
21776     }
21777     /**
21778      * The parent node for this node. @type Node
21779      */
21780     this.parentNode = null;
21781     /**
21782      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21783      */
21784     this.firstChild = null;
21785     /**
21786      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21787      */
21788     this.lastChild = null;
21789     /**
21790      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21791      */
21792     this.previousSibling = null;
21793     /**
21794      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21795      */
21796     this.nextSibling = null;
21797
21798     this.addEvents({
21799        /**
21800         * @event append
21801         * Fires when a new child node is appended
21802         * @param {Tree} tree The owner tree
21803         * @param {Node} this This node
21804         * @param {Node} node The newly appended node
21805         * @param {Number} index The index of the newly appended node
21806         */
21807        "append" : true,
21808        /**
21809         * @event remove
21810         * Fires when a child node is removed
21811         * @param {Tree} tree The owner tree
21812         * @param {Node} this This node
21813         * @param {Node} node The removed node
21814         */
21815        "remove" : true,
21816        /**
21817         * @event move
21818         * Fires when this node is moved to a new location in the tree
21819         * @param {Tree} tree The owner tree
21820         * @param {Node} this This node
21821         * @param {Node} oldParent The old parent of this node
21822         * @param {Node} newParent The new parent of this node
21823         * @param {Number} index The index it was moved to
21824         */
21825        "move" : true,
21826        /**
21827         * @event insert
21828         * Fires when a new child node is inserted.
21829         * @param {Tree} tree The owner tree
21830         * @param {Node} this This node
21831         * @param {Node} node The child node inserted
21832         * @param {Node} refNode The child node the node was inserted before
21833         */
21834        "insert" : true,
21835        /**
21836         * @event beforeappend
21837         * Fires before a new child is appended, return false to cancel the append.
21838         * @param {Tree} tree The owner tree
21839         * @param {Node} this This node
21840         * @param {Node} node The child node to be appended
21841         */
21842        "beforeappend" : true,
21843        /**
21844         * @event beforeremove
21845         * Fires before a child is removed, return false to cancel the remove.
21846         * @param {Tree} tree The owner tree
21847         * @param {Node} this This node
21848         * @param {Node} node The child node to be removed
21849         */
21850        "beforeremove" : true,
21851        /**
21852         * @event beforemove
21853         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21854         * @param {Tree} tree The owner tree
21855         * @param {Node} this This node
21856         * @param {Node} oldParent The parent of this node
21857         * @param {Node} newParent The new parent this node is moving to
21858         * @param {Number} index The index it is being moved to
21859         */
21860        "beforemove" : true,
21861        /**
21862         * @event beforeinsert
21863         * Fires before a new child is inserted, return false to cancel the insert.
21864         * @param {Tree} tree The owner tree
21865         * @param {Node} this This node
21866         * @param {Node} node The child node to be inserted
21867         * @param {Node} refNode The child node the node is being inserted before
21868         */
21869        "beforeinsert" : true
21870    });
21871     this.listeners = this.attributes.listeners;
21872     Roo.data.Node.superclass.constructor.call(this);
21873 };
21874
21875 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21876     fireEvent : function(evtName){
21877         // first do standard event for this node
21878         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21879             return false;
21880         }
21881         // then bubble it up to the tree if the event wasn't cancelled
21882         var ot = this.getOwnerTree();
21883         if(ot){
21884             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21885                 return false;
21886             }
21887         }
21888         return true;
21889     },
21890
21891     /**
21892      * Returns true if this node is a leaf
21893      * @return {Boolean}
21894      */
21895     isLeaf : function(){
21896         return this.leaf === true;
21897     },
21898
21899     // private
21900     setFirstChild : function(node){
21901         this.firstChild = node;
21902     },
21903
21904     //private
21905     setLastChild : function(node){
21906         this.lastChild = node;
21907     },
21908
21909
21910     /**
21911      * Returns true if this node is the last child of its parent
21912      * @return {Boolean}
21913      */
21914     isLast : function(){
21915        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21916     },
21917
21918     /**
21919      * Returns true if this node is the first child of its parent
21920      * @return {Boolean}
21921      */
21922     isFirst : function(){
21923        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21924     },
21925
21926     hasChildNodes : function(){
21927         return !this.isLeaf() && this.childNodes.length > 0;
21928     },
21929
21930     /**
21931      * Insert node(s) as the last child node of this node.
21932      * @param {Node/Array} node The node or Array of nodes to append
21933      * @return {Node} The appended node if single append, or null if an array was passed
21934      */
21935     appendChild : function(node){
21936         var multi = false;
21937         if(node instanceof Array){
21938             multi = node;
21939         }else if(arguments.length > 1){
21940             multi = arguments;
21941         }
21942         // if passed an array or multiple args do them one by one
21943         if(multi){
21944             for(var i = 0, len = multi.length; i < len; i++) {
21945                 this.appendChild(multi[i]);
21946             }
21947         }else{
21948             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21949                 return false;
21950             }
21951             var index = this.childNodes.length;
21952             var oldParent = node.parentNode;
21953             // it's a move, make sure we move it cleanly
21954             if(oldParent){
21955                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21956                     return false;
21957                 }
21958                 oldParent.removeChild(node);
21959             }
21960             index = this.childNodes.length;
21961             if(index == 0){
21962                 this.setFirstChild(node);
21963             }
21964             this.childNodes.push(node);
21965             node.parentNode = this;
21966             var ps = this.childNodes[index-1];
21967             if(ps){
21968                 node.previousSibling = ps;
21969                 ps.nextSibling = node;
21970             }else{
21971                 node.previousSibling = null;
21972             }
21973             node.nextSibling = null;
21974             this.setLastChild(node);
21975             node.setOwnerTree(this.getOwnerTree());
21976             this.fireEvent("append", this.ownerTree, this, node, index);
21977             if(oldParent){
21978                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21979             }
21980             return node;
21981         }
21982     },
21983
21984     /**
21985      * Removes a child node from this node.
21986      * @param {Node} node The node to remove
21987      * @return {Node} The removed node
21988      */
21989     removeChild : function(node){
21990         var index = this.childNodes.indexOf(node);
21991         if(index == -1){
21992             return false;
21993         }
21994         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21995             return false;
21996         }
21997
21998         // remove it from childNodes collection
21999         this.childNodes.splice(index, 1);
22000
22001         // update siblings
22002         if(node.previousSibling){
22003             node.previousSibling.nextSibling = node.nextSibling;
22004         }
22005         if(node.nextSibling){
22006             node.nextSibling.previousSibling = node.previousSibling;
22007         }
22008
22009         // update child refs
22010         if(this.firstChild == node){
22011             this.setFirstChild(node.nextSibling);
22012         }
22013         if(this.lastChild == node){
22014             this.setLastChild(node.previousSibling);
22015         }
22016
22017         node.setOwnerTree(null);
22018         // clear any references from the node
22019         node.parentNode = null;
22020         node.previousSibling = null;
22021         node.nextSibling = null;
22022         this.fireEvent("remove", this.ownerTree, this, node);
22023         return node;
22024     },
22025
22026     /**
22027      * Inserts the first node before the second node in this nodes childNodes collection.
22028      * @param {Node} node The node to insert
22029      * @param {Node} refNode The node to insert before (if null the node is appended)
22030      * @return {Node} The inserted node
22031      */
22032     insertBefore : function(node, refNode){
22033         if(!refNode){ // like standard Dom, refNode can be null for append
22034             return this.appendChild(node);
22035         }
22036         // nothing to do
22037         if(node == refNode){
22038             return false;
22039         }
22040
22041         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
22042             return false;
22043         }
22044         var index = this.childNodes.indexOf(refNode);
22045         var oldParent = node.parentNode;
22046         var refIndex = index;
22047
22048         // when moving internally, indexes will change after remove
22049         if(oldParent == this && this.childNodes.indexOf(node) < index){
22050             refIndex--;
22051         }
22052
22053         // it's a move, make sure we move it cleanly
22054         if(oldParent){
22055             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
22056                 return false;
22057             }
22058             oldParent.removeChild(node);
22059         }
22060         if(refIndex == 0){
22061             this.setFirstChild(node);
22062         }
22063         this.childNodes.splice(refIndex, 0, node);
22064         node.parentNode = this;
22065         var ps = this.childNodes[refIndex-1];
22066         if(ps){
22067             node.previousSibling = ps;
22068             ps.nextSibling = node;
22069         }else{
22070             node.previousSibling = null;
22071         }
22072         node.nextSibling = refNode;
22073         refNode.previousSibling = node;
22074         node.setOwnerTree(this.getOwnerTree());
22075         this.fireEvent("insert", this.ownerTree, this, node, refNode);
22076         if(oldParent){
22077             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
22078         }
22079         return node;
22080     },
22081
22082     /**
22083      * Returns the child node at the specified index.
22084      * @param {Number} index
22085      * @return {Node}
22086      */
22087     item : function(index){
22088         return this.childNodes[index];
22089     },
22090
22091     /**
22092      * Replaces one child node in this node with another.
22093      * @param {Node} newChild The replacement node
22094      * @param {Node} oldChild The node to replace
22095      * @return {Node} The replaced node
22096      */
22097     replaceChild : function(newChild, oldChild){
22098         this.insertBefore(newChild, oldChild);
22099         this.removeChild(oldChild);
22100         return oldChild;
22101     },
22102
22103     /**
22104      * Returns the index of a child node
22105      * @param {Node} node
22106      * @return {Number} The index of the node or -1 if it was not found
22107      */
22108     indexOf : function(child){
22109         return this.childNodes.indexOf(child);
22110     },
22111
22112     /**
22113      * Returns the tree this node is in.
22114      * @return {Tree}
22115      */
22116     getOwnerTree : function(){
22117         // if it doesn't have one, look for one
22118         if(!this.ownerTree){
22119             var p = this;
22120             while(p){
22121                 if(p.ownerTree){
22122                     this.ownerTree = p.ownerTree;
22123                     break;
22124                 }
22125                 p = p.parentNode;
22126             }
22127         }
22128         return this.ownerTree;
22129     },
22130
22131     /**
22132      * Returns depth of this node (the root node has a depth of 0)
22133      * @return {Number}
22134      */
22135     getDepth : function(){
22136         var depth = 0;
22137         var p = this;
22138         while(p.parentNode){
22139             ++depth;
22140             p = p.parentNode;
22141         }
22142         return depth;
22143     },
22144
22145     // private
22146     setOwnerTree : function(tree){
22147         // if it's move, we need to update everyone
22148         if(tree != this.ownerTree){
22149             if(this.ownerTree){
22150                 this.ownerTree.unregisterNode(this);
22151             }
22152             this.ownerTree = tree;
22153             var cs = this.childNodes;
22154             for(var i = 0, len = cs.length; i < len; i++) {
22155                 cs[i].setOwnerTree(tree);
22156             }
22157             if(tree){
22158                 tree.registerNode(this);
22159             }
22160         }
22161     },
22162
22163     /**
22164      * Returns the path for this node. The path can be used to expand or select this node programmatically.
22165      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
22166      * @return {String} The path
22167      */
22168     getPath : function(attr){
22169         attr = attr || "id";
22170         var p = this.parentNode;
22171         var b = [this.attributes[attr]];
22172         while(p){
22173             b.unshift(p.attributes[attr]);
22174             p = p.parentNode;
22175         }
22176         var sep = this.getOwnerTree().pathSeparator;
22177         return sep + b.join(sep);
22178     },
22179
22180     /**
22181      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
22182      * function call will be the scope provided or the current node. The arguments to the function
22183      * will be the args provided or the current node. If the function returns false at any point,
22184      * the bubble is stopped.
22185      * @param {Function} fn The function to call
22186      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22187      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22188      */
22189     bubble : function(fn, scope, args){
22190         var p = this;
22191         while(p){
22192             if(fn.call(scope || p, args || p) === false){
22193                 break;
22194             }
22195             p = p.parentNode;
22196         }
22197     },
22198
22199     /**
22200      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
22201      * function call will be the scope provided or the current node. The arguments to the function
22202      * will be the args provided or the current node. If the function returns false at any point,
22203      * the cascade is stopped on that branch.
22204      * @param {Function} fn The function to call
22205      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22206      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22207      */
22208     cascade : function(fn, scope, args){
22209         if(fn.call(scope || this, args || this) !== false){
22210             var cs = this.childNodes;
22211             for(var i = 0, len = cs.length; i < len; i++) {
22212                 cs[i].cascade(fn, scope, args);
22213             }
22214         }
22215     },
22216
22217     /**
22218      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
22219      * function call will be the scope provided or the current node. The arguments to the function
22220      * will be the args provided or the current node. If the function returns false at any point,
22221      * the iteration stops.
22222      * @param {Function} fn The function to call
22223      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22224      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22225      */
22226     eachChild : function(fn, scope, args){
22227         var cs = this.childNodes;
22228         for(var i = 0, len = cs.length; i < len; i++) {
22229                 if(fn.call(scope || this, args || cs[i]) === false){
22230                     break;
22231                 }
22232         }
22233     },
22234
22235     /**
22236      * Finds the first child that has the attribute with the specified value.
22237      * @param {String} attribute The attribute name
22238      * @param {Mixed} value The value to search for
22239      * @return {Node} The found child or null if none was found
22240      */
22241     findChild : function(attribute, value){
22242         var cs = this.childNodes;
22243         for(var i = 0, len = cs.length; i < len; i++) {
22244                 if(cs[i].attributes[attribute] == value){
22245                     return cs[i];
22246                 }
22247         }
22248         return null;
22249     },
22250
22251     /**
22252      * Finds the first child by a custom function. The child matches if the function passed
22253      * returns true.
22254      * @param {Function} fn
22255      * @param {Object} scope (optional)
22256      * @return {Node} The found child or null if none was found
22257      */
22258     findChildBy : function(fn, scope){
22259         var cs = this.childNodes;
22260         for(var i = 0, len = cs.length; i < len; i++) {
22261                 if(fn.call(scope||cs[i], cs[i]) === true){
22262                     return cs[i];
22263                 }
22264         }
22265         return null;
22266     },
22267
22268     /**
22269      * Sorts this nodes children using the supplied sort function
22270      * @param {Function} fn
22271      * @param {Object} scope (optional)
22272      */
22273     sort : function(fn, scope){
22274         var cs = this.childNodes;
22275         var len = cs.length;
22276         if(len > 0){
22277             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
22278             cs.sort(sortFn);
22279             for(var i = 0; i < len; i++){
22280                 var n = cs[i];
22281                 n.previousSibling = cs[i-1];
22282                 n.nextSibling = cs[i+1];
22283                 if(i == 0){
22284                     this.setFirstChild(n);
22285                 }
22286                 if(i == len-1){
22287                     this.setLastChild(n);
22288                 }
22289             }
22290         }
22291     },
22292
22293     /**
22294      * Returns true if this node is an ancestor (at any point) of the passed node.
22295      * @param {Node} node
22296      * @return {Boolean}
22297      */
22298     contains : function(node){
22299         return node.isAncestor(this);
22300     },
22301
22302     /**
22303      * Returns true if the passed node is an ancestor (at any point) of this node.
22304      * @param {Node} node
22305      * @return {Boolean}
22306      */
22307     isAncestor : function(node){
22308         var p = this.parentNode;
22309         while(p){
22310             if(p == node){
22311                 return true;
22312             }
22313             p = p.parentNode;
22314         }
22315         return false;
22316     },
22317
22318     toString : function(){
22319         return "[Node"+(this.id?" "+this.id:"")+"]";
22320     }
22321 });/*
22322  * Based on:
22323  * Ext JS Library 1.1.1
22324  * Copyright(c) 2006-2007, Ext JS, LLC.
22325  *
22326  * Originally Released Under LGPL - original licence link has changed is not relivant.
22327  *
22328  * Fork - LGPL
22329  * <script type="text/javascript">
22330  */
22331  
22332
22333 /**
22334  * @class Roo.ComponentMgr
22335  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
22336  * @singleton
22337  */
22338 Roo.ComponentMgr = function(){
22339     var all = new Roo.util.MixedCollection();
22340
22341     return {
22342         /**
22343          * Registers a component.
22344          * @param {Roo.Component} c The component
22345          */
22346         register : function(c){
22347             all.add(c);
22348         },
22349
22350         /**
22351          * Unregisters a component.
22352          * @param {Roo.Component} c The component
22353          */
22354         unregister : function(c){
22355             all.remove(c);
22356         },
22357
22358         /**
22359          * Returns a component by id
22360          * @param {String} id The component id
22361          */
22362         get : function(id){
22363             return all.get(id);
22364         },
22365
22366         /**
22367          * Registers a function that will be called when a specified component is added to ComponentMgr
22368          * @param {String} id The component id
22369          * @param {Funtction} fn The callback function
22370          * @param {Object} scope The scope of the callback
22371          */
22372         onAvailable : function(id, fn, scope){
22373             all.on("add", function(index, o){
22374                 if(o.id == id){
22375                     fn.call(scope || o, o);
22376                     all.un("add", fn, scope);
22377                 }
22378             });
22379         }
22380     };
22381 }();/*
22382  * Based on:
22383  * Ext JS Library 1.1.1
22384  * Copyright(c) 2006-2007, Ext JS, LLC.
22385  *
22386  * Originally Released Under LGPL - original licence link has changed is not relivant.
22387  *
22388  * Fork - LGPL
22389  * <script type="text/javascript">
22390  */
22391  
22392 /**
22393  * @class Roo.Component
22394  * @extends Roo.util.Observable
22395  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
22396  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
22397  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
22398  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
22399  * All visual components (widgets) that require rendering into a layout should subclass Component.
22400  * @constructor
22401  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
22402  * 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
22403  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
22404  */
22405 Roo.Component = function(config){
22406     config = config || {};
22407     if(config.tagName || config.dom || typeof config == "string"){ // element object
22408         config = {el: config, id: config.id || config};
22409     }
22410     this.initialConfig = config;
22411
22412     Roo.apply(this, config);
22413     this.addEvents({
22414         /**
22415          * @event disable
22416          * Fires after the component is disabled.
22417              * @param {Roo.Component} this
22418              */
22419         disable : true,
22420         /**
22421          * @event enable
22422          * Fires after the component is enabled.
22423              * @param {Roo.Component} this
22424              */
22425         enable : true,
22426         /**
22427          * @event beforeshow
22428          * Fires before the component is shown.  Return false to stop the show.
22429              * @param {Roo.Component} this
22430              */
22431         beforeshow : true,
22432         /**
22433          * @event show
22434          * Fires after the component is shown.
22435              * @param {Roo.Component} this
22436              */
22437         show : true,
22438         /**
22439          * @event beforehide
22440          * Fires before the component is hidden. Return false to stop the hide.
22441              * @param {Roo.Component} this
22442              */
22443         beforehide : true,
22444         /**
22445          * @event hide
22446          * Fires after the component is hidden.
22447              * @param {Roo.Component} this
22448              */
22449         hide : true,
22450         /**
22451          * @event beforerender
22452          * Fires before the component is rendered. Return false to stop the render.
22453              * @param {Roo.Component} this
22454              */
22455         beforerender : true,
22456         /**
22457          * @event render
22458          * Fires after the component is rendered.
22459              * @param {Roo.Component} this
22460              */
22461         render : true,
22462         /**
22463          * @event beforedestroy
22464          * Fires before the component is destroyed. Return false to stop the destroy.
22465              * @param {Roo.Component} this
22466              */
22467         beforedestroy : true,
22468         /**
22469          * @event destroy
22470          * Fires after the component is destroyed.
22471              * @param {Roo.Component} this
22472              */
22473         destroy : true
22474     });
22475     if(!this.id){
22476         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
22477     }
22478     Roo.ComponentMgr.register(this);
22479     Roo.Component.superclass.constructor.call(this);
22480     this.initComponent();
22481     if(this.renderTo){ // not supported by all components yet. use at your own risk!
22482         this.render(this.renderTo);
22483         delete this.renderTo;
22484     }
22485 };
22486
22487 /** @private */
22488 Roo.Component.AUTO_ID = 1000;
22489
22490 Roo.extend(Roo.Component, Roo.util.Observable, {
22491     /**
22492      * @scope Roo.Component.prototype
22493      * @type {Boolean}
22494      * true if this component is hidden. Read-only.
22495      */
22496     hidden : false,
22497     /**
22498      * @type {Boolean}
22499      * true if this component is disabled. Read-only.
22500      */
22501     disabled : false,
22502     /**
22503      * @type {Boolean}
22504      * true if this component has been rendered. Read-only.
22505      */
22506     rendered : false,
22507     
22508     /** @cfg {String} disableClass
22509      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
22510      */
22511     disabledClass : "x-item-disabled",
22512         /** @cfg {Boolean} allowDomMove
22513          * Whether the component can move the Dom node when rendering (defaults to true).
22514          */
22515     allowDomMove : true,
22516     /** @cfg {String} hideMode
22517      * How this component should hidden. Supported values are
22518      * "visibility" (css visibility), "offsets" (negative offset position) and
22519      * "display" (css display) - defaults to "display".
22520      */
22521     hideMode: 'display',
22522
22523     /** @private */
22524     ctype : "Roo.Component",
22525
22526     /**
22527      * @cfg {String} actionMode 
22528      * which property holds the element that used for  hide() / show() / disable() / enable()
22529      * default is 'el' 
22530      */
22531     actionMode : "el",
22532
22533     /** @private */
22534     getActionEl : function(){
22535         return this[this.actionMode];
22536     },
22537
22538     initComponent : Roo.emptyFn,
22539     /**
22540      * If this is a lazy rendering component, render it to its container element.
22541      * @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.
22542      */
22543     render : function(container, position){
22544         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
22545             if(!container && this.el){
22546                 this.el = Roo.get(this.el);
22547                 container = this.el.dom.parentNode;
22548                 this.allowDomMove = false;
22549             }
22550             this.container = Roo.get(container);
22551             this.rendered = true;
22552             if(position !== undefined){
22553                 if(typeof position == 'number'){
22554                     position = this.container.dom.childNodes[position];
22555                 }else{
22556                     position = Roo.getDom(position);
22557                 }
22558             }
22559             this.onRender(this.container, position || null);
22560             if(this.cls){
22561                 this.el.addClass(this.cls);
22562                 delete this.cls;
22563             }
22564             if(this.style){
22565                 this.el.applyStyles(this.style);
22566                 delete this.style;
22567             }
22568             this.fireEvent("render", this);
22569             this.afterRender(this.container);
22570             if(this.hidden){
22571                 this.hide();
22572             }
22573             if(this.disabled){
22574                 this.disable();
22575             }
22576         }
22577         return this;
22578     },
22579
22580     /** @private */
22581     // default function is not really useful
22582     onRender : function(ct, position){
22583         if(this.el){
22584             this.el = Roo.get(this.el);
22585             if(this.allowDomMove !== false){
22586                 ct.dom.insertBefore(this.el.dom, position);
22587             }
22588         }
22589     },
22590
22591     /** @private */
22592     getAutoCreate : function(){
22593         var cfg = typeof this.autoCreate == "object" ?
22594                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
22595         if(this.id && !cfg.id){
22596             cfg.id = this.id;
22597         }
22598         return cfg;
22599     },
22600
22601     /** @private */
22602     afterRender : Roo.emptyFn,
22603
22604     /**
22605      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22606      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22607      */
22608     destroy : function(){
22609         if(this.fireEvent("beforedestroy", this) !== false){
22610             this.purgeListeners();
22611             this.beforeDestroy();
22612             if(this.rendered){
22613                 this.el.removeAllListeners();
22614                 this.el.remove();
22615                 if(this.actionMode == "container"){
22616                     this.container.remove();
22617                 }
22618             }
22619             this.onDestroy();
22620             Roo.ComponentMgr.unregister(this);
22621             this.fireEvent("destroy", this);
22622         }
22623     },
22624
22625         /** @private */
22626     beforeDestroy : function(){
22627
22628     },
22629
22630         /** @private */
22631         onDestroy : function(){
22632
22633     },
22634
22635     /**
22636      * Returns the underlying {@link Roo.Element}.
22637      * @return {Roo.Element} The element
22638      */
22639     getEl : function(){
22640         return this.el;
22641     },
22642
22643     /**
22644      * Returns the id of this component.
22645      * @return {String}
22646      */
22647     getId : function(){
22648         return this.id;
22649     },
22650
22651     /**
22652      * Try to focus this component.
22653      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22654      * @return {Roo.Component} this
22655      */
22656     focus : function(selectText){
22657         if(this.rendered){
22658             this.el.focus();
22659             if(selectText === true){
22660                 this.el.dom.select();
22661             }
22662         }
22663         return this;
22664     },
22665
22666     /** @private */
22667     blur : function(){
22668         if(this.rendered){
22669             this.el.blur();
22670         }
22671         return this;
22672     },
22673
22674     /**
22675      * Disable this component.
22676      * @return {Roo.Component} this
22677      */
22678     disable : function(){
22679         if(this.rendered){
22680             this.onDisable();
22681         }
22682         this.disabled = true;
22683         this.fireEvent("disable", this);
22684         return this;
22685     },
22686
22687         // private
22688     onDisable : function(){
22689         this.getActionEl().addClass(this.disabledClass);
22690         this.el.dom.disabled = true;
22691     },
22692
22693     /**
22694      * Enable this component.
22695      * @return {Roo.Component} this
22696      */
22697     enable : function(){
22698         if(this.rendered){
22699             this.onEnable();
22700         }
22701         this.disabled = false;
22702         this.fireEvent("enable", this);
22703         return this;
22704     },
22705
22706         // private
22707     onEnable : function(){
22708         this.getActionEl().removeClass(this.disabledClass);
22709         this.el.dom.disabled = false;
22710     },
22711
22712     /**
22713      * Convenience function for setting disabled/enabled by boolean.
22714      * @param {Boolean} disabled
22715      */
22716     setDisabled : function(disabled){
22717         this[disabled ? "disable" : "enable"]();
22718     },
22719
22720     /**
22721      * Show this component.
22722      * @return {Roo.Component} this
22723      */
22724     show: function(){
22725         if(this.fireEvent("beforeshow", this) !== false){
22726             this.hidden = false;
22727             if(this.rendered){
22728                 this.onShow();
22729             }
22730             this.fireEvent("show", this);
22731         }
22732         return this;
22733     },
22734
22735     // private
22736     onShow : function(){
22737         var ae = this.getActionEl();
22738         if(this.hideMode == 'visibility'){
22739             ae.dom.style.visibility = "visible";
22740         }else if(this.hideMode == 'offsets'){
22741             ae.removeClass('x-hidden');
22742         }else{
22743             ae.dom.style.display = "";
22744         }
22745     },
22746
22747     /**
22748      * Hide this component.
22749      * @return {Roo.Component} this
22750      */
22751     hide: function(){
22752         if(this.fireEvent("beforehide", this) !== false){
22753             this.hidden = true;
22754             if(this.rendered){
22755                 this.onHide();
22756             }
22757             this.fireEvent("hide", this);
22758         }
22759         return this;
22760     },
22761
22762     // private
22763     onHide : function(){
22764         var ae = this.getActionEl();
22765         if(this.hideMode == 'visibility'){
22766             ae.dom.style.visibility = "hidden";
22767         }else if(this.hideMode == 'offsets'){
22768             ae.addClass('x-hidden');
22769         }else{
22770             ae.dom.style.display = "none";
22771         }
22772     },
22773
22774     /**
22775      * Convenience function to hide or show this component by boolean.
22776      * @param {Boolean} visible True to show, false to hide
22777      * @return {Roo.Component} this
22778      */
22779     setVisible: function(visible){
22780         if(visible) {
22781             this.show();
22782         }else{
22783             this.hide();
22784         }
22785         return this;
22786     },
22787
22788     /**
22789      * Returns true if this component is visible.
22790      */
22791     isVisible : function(){
22792         return this.getActionEl().isVisible();
22793     },
22794
22795     cloneConfig : function(overrides){
22796         overrides = overrides || {};
22797         var id = overrides.id || Roo.id();
22798         var cfg = Roo.applyIf(overrides, this.initialConfig);
22799         cfg.id = id; // prevent dup id
22800         return new this.constructor(cfg);
22801     }
22802 });/*
22803  * Based on:
22804  * Ext JS Library 1.1.1
22805  * Copyright(c) 2006-2007, Ext JS, LLC.
22806  *
22807  * Originally Released Under LGPL - original licence link has changed is not relivant.
22808  *
22809  * Fork - LGPL
22810  * <script type="text/javascript">
22811  */
22812  (function(){ 
22813 /**
22814  * @class Roo.Layer
22815  * @extends Roo.Element
22816  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22817  * automatic maintaining of shadow/shim positions.
22818  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22819  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22820  * you can pass a string with a CSS class name. False turns off the shadow.
22821  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22822  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22823  * @cfg {String} cls CSS class to add to the element
22824  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22825  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22826  * @constructor
22827  * @param {Object} config An object with config options.
22828  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22829  */
22830
22831 Roo.Layer = function(config, existingEl){
22832     config = config || {};
22833     var dh = Roo.DomHelper;
22834     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22835     if(existingEl){
22836         this.dom = Roo.getDom(existingEl);
22837     }
22838     if(!this.dom){
22839         var o = config.dh || {tag: "div", cls: "x-layer"};
22840         this.dom = dh.append(pel, o);
22841     }
22842     if(config.cls){
22843         this.addClass(config.cls);
22844     }
22845     this.constrain = config.constrain !== false;
22846     this.visibilityMode = Roo.Element.VISIBILITY;
22847     if(config.id){
22848         this.id = this.dom.id = config.id;
22849     }else{
22850         this.id = Roo.id(this.dom);
22851     }
22852     this.zindex = config.zindex || this.getZIndex();
22853     this.position("absolute", this.zindex);
22854     if(config.shadow){
22855         this.shadowOffset = config.shadowOffset || 4;
22856         this.shadow = new Roo.Shadow({
22857             offset : this.shadowOffset,
22858             mode : config.shadow
22859         });
22860     }else{
22861         this.shadowOffset = 0;
22862     }
22863     this.useShim = config.shim !== false && Roo.useShims;
22864     this.useDisplay = config.useDisplay;
22865     this.hide();
22866 };
22867
22868 var supr = Roo.Element.prototype;
22869
22870 // shims are shared among layer to keep from having 100 iframes
22871 var shims = [];
22872
22873 Roo.extend(Roo.Layer, Roo.Element, {
22874
22875     getZIndex : function(){
22876         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22877     },
22878
22879     getShim : function(){
22880         if(!this.useShim){
22881             return null;
22882         }
22883         if(this.shim){
22884             return this.shim;
22885         }
22886         var shim = shims.shift();
22887         if(!shim){
22888             shim = this.createShim();
22889             shim.enableDisplayMode('block');
22890             shim.dom.style.display = 'none';
22891             shim.dom.style.visibility = 'visible';
22892         }
22893         var pn = this.dom.parentNode;
22894         if(shim.dom.parentNode != pn){
22895             pn.insertBefore(shim.dom, this.dom);
22896         }
22897         shim.setStyle('z-index', this.getZIndex()-2);
22898         this.shim = shim;
22899         return shim;
22900     },
22901
22902     hideShim : function(){
22903         if(this.shim){
22904             this.shim.setDisplayed(false);
22905             shims.push(this.shim);
22906             delete this.shim;
22907         }
22908     },
22909
22910     disableShadow : function(){
22911         if(this.shadow){
22912             this.shadowDisabled = true;
22913             this.shadow.hide();
22914             this.lastShadowOffset = this.shadowOffset;
22915             this.shadowOffset = 0;
22916         }
22917     },
22918
22919     enableShadow : function(show){
22920         if(this.shadow){
22921             this.shadowDisabled = false;
22922             this.shadowOffset = this.lastShadowOffset;
22923             delete this.lastShadowOffset;
22924             if(show){
22925                 this.sync(true);
22926             }
22927         }
22928     },
22929
22930     // private
22931     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22932     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22933     sync : function(doShow){
22934         var sw = this.shadow;
22935         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22936             var sh = this.getShim();
22937
22938             var w = this.getWidth(),
22939                 h = this.getHeight();
22940
22941             var l = this.getLeft(true),
22942                 t = this.getTop(true);
22943
22944             if(sw && !this.shadowDisabled){
22945                 if(doShow && !sw.isVisible()){
22946                     sw.show(this);
22947                 }else{
22948                     sw.realign(l, t, w, h);
22949                 }
22950                 if(sh){
22951                     if(doShow){
22952                        sh.show();
22953                     }
22954                     // fit the shim behind the shadow, so it is shimmed too
22955                     var a = sw.adjusts, s = sh.dom.style;
22956                     s.left = (Math.min(l, l+a.l))+"px";
22957                     s.top = (Math.min(t, t+a.t))+"px";
22958                     s.width = (w+a.w)+"px";
22959                     s.height = (h+a.h)+"px";
22960                 }
22961             }else if(sh){
22962                 if(doShow){
22963                    sh.show();
22964                 }
22965                 sh.setSize(w, h);
22966                 sh.setLeftTop(l, t);
22967             }
22968             
22969         }
22970     },
22971
22972     // private
22973     destroy : function(){
22974         this.hideShim();
22975         if(this.shadow){
22976             this.shadow.hide();
22977         }
22978         this.removeAllListeners();
22979         var pn = this.dom.parentNode;
22980         if(pn){
22981             pn.removeChild(this.dom);
22982         }
22983         Roo.Element.uncache(this.id);
22984     },
22985
22986     remove : function(){
22987         this.destroy();
22988     },
22989
22990     // private
22991     beginUpdate : function(){
22992         this.updating = true;
22993     },
22994
22995     // private
22996     endUpdate : function(){
22997         this.updating = false;
22998         this.sync(true);
22999     },
23000
23001     // private
23002     hideUnders : function(negOffset){
23003         if(this.shadow){
23004             this.shadow.hide();
23005         }
23006         this.hideShim();
23007     },
23008
23009     // private
23010     constrainXY : function(){
23011         if(this.constrain){
23012             var vw = Roo.lib.Dom.getViewWidth(),
23013                 vh = Roo.lib.Dom.getViewHeight();
23014             var s = Roo.get(document).getScroll();
23015
23016             var xy = this.getXY();
23017             var x = xy[0], y = xy[1];   
23018             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23019             // only move it if it needs it
23020             var moved = false;
23021             // first validate right/bottom
23022             if((x + w) > vw+s.left){
23023                 x = vw - w - this.shadowOffset;
23024                 moved = true;
23025             }
23026             if((y + h) > vh+s.top){
23027                 y = vh - h - this.shadowOffset;
23028                 moved = true;
23029             }
23030             // then make sure top/left isn't negative
23031             if(x < s.left){
23032                 x = s.left;
23033                 moved = true;
23034             }
23035             if(y < s.top){
23036                 y = s.top;
23037                 moved = true;
23038             }
23039             if(moved){
23040                 if(this.avoidY){
23041                     var ay = this.avoidY;
23042                     if(y <= ay && (y+h) >= ay){
23043                         y = ay-h-5;   
23044                     }
23045                 }
23046                 xy = [x, y];
23047                 this.storeXY(xy);
23048                 supr.setXY.call(this, xy);
23049                 this.sync();
23050             }
23051         }
23052     },
23053
23054     isVisible : function(){
23055         return this.visible;    
23056     },
23057
23058     // private
23059     showAction : function(){
23060         this.visible = true; // track visibility to prevent getStyle calls
23061         if(this.useDisplay === true){
23062             this.setDisplayed("");
23063         }else if(this.lastXY){
23064             supr.setXY.call(this, this.lastXY);
23065         }else if(this.lastLT){
23066             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23067         }
23068     },
23069
23070     // private
23071     hideAction : function(){
23072         this.visible = false;
23073         if(this.useDisplay === true){
23074             this.setDisplayed(false);
23075         }else{
23076             this.setLeftTop(-10000,-10000);
23077         }
23078     },
23079
23080     // overridden Element method
23081     setVisible : function(v, a, d, c, e){
23082         if(v){
23083             this.showAction();
23084         }
23085         if(a && v){
23086             var cb = function(){
23087                 this.sync(true);
23088                 if(c){
23089                     c();
23090                 }
23091             }.createDelegate(this);
23092             supr.setVisible.call(this, true, true, d, cb, e);
23093         }else{
23094             if(!v){
23095                 this.hideUnders(true);
23096             }
23097             var cb = c;
23098             if(a){
23099                 cb = function(){
23100                     this.hideAction();
23101                     if(c){
23102                         c();
23103                     }
23104                 }.createDelegate(this);
23105             }
23106             supr.setVisible.call(this, v, a, d, cb, e);
23107             if(v){
23108                 this.sync(true);
23109             }else if(!a){
23110                 this.hideAction();
23111             }
23112         }
23113     },
23114
23115     storeXY : function(xy){
23116         delete this.lastLT;
23117         this.lastXY = xy;
23118     },
23119
23120     storeLeftTop : function(left, top){
23121         delete this.lastXY;
23122         this.lastLT = [left, top];
23123     },
23124
23125     // private
23126     beforeFx : function(){
23127         this.beforeAction();
23128         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23129     },
23130
23131     // private
23132     afterFx : function(){
23133         Roo.Layer.superclass.afterFx.apply(this, arguments);
23134         this.sync(this.isVisible());
23135     },
23136
23137     // private
23138     beforeAction : function(){
23139         if(!this.updating && this.shadow){
23140             this.shadow.hide();
23141         }
23142     },
23143
23144     // overridden Element method
23145     setLeft : function(left){
23146         this.storeLeftTop(left, this.getTop(true));
23147         supr.setLeft.apply(this, arguments);
23148         this.sync();
23149     },
23150
23151     setTop : function(top){
23152         this.storeLeftTop(this.getLeft(true), top);
23153         supr.setTop.apply(this, arguments);
23154         this.sync();
23155     },
23156
23157     setLeftTop : function(left, top){
23158         this.storeLeftTop(left, top);
23159         supr.setLeftTop.apply(this, arguments);
23160         this.sync();
23161     },
23162
23163     setXY : function(xy, a, d, c, e){
23164         this.fixDisplay();
23165         this.beforeAction();
23166         this.storeXY(xy);
23167         var cb = this.createCB(c);
23168         supr.setXY.call(this, xy, a, d, cb, e);
23169         if(!a){
23170             cb();
23171         }
23172     },
23173
23174     // private
23175     createCB : function(c){
23176         var el = this;
23177         return function(){
23178             el.constrainXY();
23179             el.sync(true);
23180             if(c){
23181                 c();
23182             }
23183         };
23184     },
23185
23186     // overridden Element method
23187     setX : function(x, a, d, c, e){
23188         this.setXY([x, this.getY()], a, d, c, e);
23189     },
23190
23191     // overridden Element method
23192     setY : function(y, a, d, c, e){
23193         this.setXY([this.getX(), y], a, d, c, e);
23194     },
23195
23196     // overridden Element method
23197     setSize : function(w, h, a, d, c, e){
23198         this.beforeAction();
23199         var cb = this.createCB(c);
23200         supr.setSize.call(this, w, h, a, d, cb, e);
23201         if(!a){
23202             cb();
23203         }
23204     },
23205
23206     // overridden Element method
23207     setWidth : function(w, a, d, c, e){
23208         this.beforeAction();
23209         var cb = this.createCB(c);
23210         supr.setWidth.call(this, w, a, d, cb, e);
23211         if(!a){
23212             cb();
23213         }
23214     },
23215
23216     // overridden Element method
23217     setHeight : function(h, a, d, c, e){
23218         this.beforeAction();
23219         var cb = this.createCB(c);
23220         supr.setHeight.call(this, h, a, d, cb, e);
23221         if(!a){
23222             cb();
23223         }
23224     },
23225
23226     // overridden Element method
23227     setBounds : function(x, y, w, h, a, d, c, e){
23228         this.beforeAction();
23229         var cb = this.createCB(c);
23230         if(!a){
23231             this.storeXY([x, y]);
23232             supr.setXY.call(this, [x, y]);
23233             supr.setSize.call(this, w, h, a, d, cb, e);
23234             cb();
23235         }else{
23236             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
23237         }
23238         return this;
23239     },
23240     
23241     /**
23242      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
23243      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
23244      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
23245      * @param {Number} zindex The new z-index to set
23246      * @return {this} The Layer
23247      */
23248     setZIndex : function(zindex){
23249         this.zindex = zindex;
23250         this.setStyle("z-index", zindex + 2);
23251         if(this.shadow){
23252             this.shadow.setZIndex(zindex + 1);
23253         }
23254         if(this.shim){
23255             this.shim.setStyle("z-index", zindex);
23256         }
23257     }
23258 });
23259 })();/*
23260  * Based on:
23261  * Ext JS Library 1.1.1
23262  * Copyright(c) 2006-2007, Ext JS, LLC.
23263  *
23264  * Originally Released Under LGPL - original licence link has changed is not relivant.
23265  *
23266  * Fork - LGPL
23267  * <script type="text/javascript">
23268  */
23269
23270
23271 /**
23272  * @class Roo.Shadow
23273  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
23274  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
23275  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
23276  * @constructor
23277  * Create a new Shadow
23278  * @param {Object} config The config object
23279  */
23280 Roo.Shadow = function(config){
23281     Roo.apply(this, config);
23282     if(typeof this.mode != "string"){
23283         this.mode = this.defaultMode;
23284     }
23285     var o = this.offset, a = {h: 0};
23286     var rad = Math.floor(this.offset/2);
23287     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
23288         case "drop":
23289             a.w = 0;
23290             a.l = a.t = o;
23291             a.t -= 1;
23292             if(Roo.isIE){
23293                 a.l -= this.offset + rad;
23294                 a.t -= this.offset + rad;
23295                 a.w -= rad;
23296                 a.h -= rad;
23297                 a.t += 1;
23298             }
23299         break;
23300         case "sides":
23301             a.w = (o*2);
23302             a.l = -o;
23303             a.t = o-1;
23304             if(Roo.isIE){
23305                 a.l -= (this.offset - rad);
23306                 a.t -= this.offset + rad;
23307                 a.l += 1;
23308                 a.w -= (this.offset - rad)*2;
23309                 a.w -= rad + 1;
23310                 a.h -= 1;
23311             }
23312         break;
23313         case "frame":
23314             a.w = a.h = (o*2);
23315             a.l = a.t = -o;
23316             a.t += 1;
23317             a.h -= 2;
23318             if(Roo.isIE){
23319                 a.l -= (this.offset - rad);
23320                 a.t -= (this.offset - rad);
23321                 a.l += 1;
23322                 a.w -= (this.offset + rad + 1);
23323                 a.h -= (this.offset + rad);
23324                 a.h += 1;
23325             }
23326         break;
23327     };
23328
23329     this.adjusts = a;
23330 };
23331
23332 Roo.Shadow.prototype = {
23333     /**
23334      * @cfg {String} mode
23335      * The shadow display mode.  Supports the following options:<br />
23336      * sides: Shadow displays on both sides and bottom only<br />
23337      * frame: Shadow displays equally on all four sides<br />
23338      * drop: Traditional bottom-right drop shadow (default)
23339      */
23340     /**
23341      * @cfg {String} offset
23342      * The number of pixels to offset the shadow from the element (defaults to 4)
23343      */
23344     offset: 4,
23345
23346     // private
23347     defaultMode: "drop",
23348
23349     /**
23350      * Displays the shadow under the target element
23351      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
23352      */
23353     show : function(target){
23354         target = Roo.get(target);
23355         if(!this.el){
23356             this.el = Roo.Shadow.Pool.pull();
23357             if(this.el.dom.nextSibling != target.dom){
23358                 this.el.insertBefore(target);
23359             }
23360         }
23361         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
23362         if(Roo.isIE){
23363             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
23364         }
23365         this.realign(
23366             target.getLeft(true),
23367             target.getTop(true),
23368             target.getWidth(),
23369             target.getHeight()
23370         );
23371         this.el.dom.style.display = "block";
23372     },
23373
23374     /**
23375      * Returns true if the shadow is visible, else false
23376      */
23377     isVisible : function(){
23378         return this.el ? true : false;  
23379     },
23380
23381     /**
23382      * Direct alignment when values are already available. Show must be called at least once before
23383      * calling this method to ensure it is initialized.
23384      * @param {Number} left The target element left position
23385      * @param {Number} top The target element top position
23386      * @param {Number} width The target element width
23387      * @param {Number} height The target element height
23388      */
23389     realign : function(l, t, w, h){
23390         if(!this.el){
23391             return;
23392         }
23393         var a = this.adjusts, d = this.el.dom, s = d.style;
23394         var iea = 0;
23395         s.left = (l+a.l)+"px";
23396         s.top = (t+a.t)+"px";
23397         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
23398  
23399         if(s.width != sws || s.height != shs){
23400             s.width = sws;
23401             s.height = shs;
23402             if(!Roo.isIE){
23403                 var cn = d.childNodes;
23404                 var sww = Math.max(0, (sw-12))+"px";
23405                 cn[0].childNodes[1].style.width = sww;
23406                 cn[1].childNodes[1].style.width = sww;
23407                 cn[2].childNodes[1].style.width = sww;
23408                 cn[1].style.height = Math.max(0, (sh-12))+"px";
23409             }
23410         }
23411     },
23412
23413     /**
23414      * Hides this shadow
23415      */
23416     hide : function(){
23417         if(this.el){
23418             this.el.dom.style.display = "none";
23419             Roo.Shadow.Pool.push(this.el);
23420             delete this.el;
23421         }
23422     },
23423
23424     /**
23425      * Adjust the z-index of this shadow
23426      * @param {Number} zindex The new z-index
23427      */
23428     setZIndex : function(z){
23429         this.zIndex = z;
23430         if(this.el){
23431             this.el.setStyle("z-index", z);
23432         }
23433     }
23434 };
23435
23436 // Private utility class that manages the internal Shadow cache
23437 Roo.Shadow.Pool = function(){
23438     var p = [];
23439     var markup = Roo.isIE ?
23440                  '<div class="x-ie-shadow"></div>' :
23441                  '<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>';
23442     return {
23443         pull : function(){
23444             var sh = p.shift();
23445             if(!sh){
23446                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
23447                 sh.autoBoxAdjust = false;
23448             }
23449             return sh;
23450         },
23451
23452         push : function(sh){
23453             p.push(sh);
23454         }
23455     };
23456 }();/*
23457  * Based on:
23458  * Ext JS Library 1.1.1
23459  * Copyright(c) 2006-2007, Ext JS, LLC.
23460  *
23461  * Originally Released Under LGPL - original licence link has changed is not relivant.
23462  *
23463  * Fork - LGPL
23464  * <script type="text/javascript">
23465  */
23466
23467 /**
23468  * @class Roo.BoxComponent
23469  * @extends Roo.Component
23470  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
23471  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
23472  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
23473  * layout containers.
23474  * @constructor
23475  * @param {Roo.Element/String/Object} config The configuration options.
23476  */
23477 Roo.BoxComponent = function(config){
23478     Roo.Component.call(this, config);
23479     this.addEvents({
23480         /**
23481          * @event resize
23482          * Fires after the component is resized.
23483              * @param {Roo.Component} this
23484              * @param {Number} adjWidth The box-adjusted width that was set
23485              * @param {Number} adjHeight The box-adjusted height that was set
23486              * @param {Number} rawWidth The width that was originally specified
23487              * @param {Number} rawHeight The height that was originally specified
23488              */
23489         resize : true,
23490         /**
23491          * @event move
23492          * Fires after the component is moved.
23493              * @param {Roo.Component} this
23494              * @param {Number} x The new x position
23495              * @param {Number} y The new y position
23496              */
23497         move : true
23498     });
23499 };
23500
23501 Roo.extend(Roo.BoxComponent, Roo.Component, {
23502     // private, set in afterRender to signify that the component has been rendered
23503     boxReady : false,
23504     // private, used to defer height settings to subclasses
23505     deferHeight: false,
23506     /** @cfg {Number} width
23507      * width (optional) size of component
23508      */
23509      /** @cfg {Number} height
23510      * height (optional) size of component
23511      */
23512      
23513     /**
23514      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
23515      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
23516      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
23517      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
23518      * @return {Roo.BoxComponent} this
23519      */
23520     setSize : function(w, h){
23521         // support for standard size objects
23522         if(typeof w == 'object'){
23523             h = w.height;
23524             w = w.width;
23525         }
23526         // not rendered
23527         if(!this.boxReady){
23528             this.width = w;
23529             this.height = h;
23530             return this;
23531         }
23532
23533         // prevent recalcs when not needed
23534         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
23535             return this;
23536         }
23537         this.lastSize = {width: w, height: h};
23538
23539         var adj = this.adjustSize(w, h);
23540         var aw = adj.width, ah = adj.height;
23541         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
23542             var rz = this.getResizeEl();
23543             if(!this.deferHeight && aw !== undefined && ah !== undefined){
23544                 rz.setSize(aw, ah);
23545             }else if(!this.deferHeight && ah !== undefined){
23546                 rz.setHeight(ah);
23547             }else if(aw !== undefined){
23548                 rz.setWidth(aw);
23549             }
23550             this.onResize(aw, ah, w, h);
23551             this.fireEvent('resize', this, aw, ah, w, h);
23552         }
23553         return this;
23554     },
23555
23556     /**
23557      * Gets the current size of the component's underlying element.
23558      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
23559      */
23560     getSize : function(){
23561         return this.el.getSize();
23562     },
23563
23564     /**
23565      * Gets the current XY position of the component's underlying element.
23566      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23567      * @return {Array} The XY position of the element (e.g., [100, 200])
23568      */
23569     getPosition : function(local){
23570         if(local === true){
23571             return [this.el.getLeft(true), this.el.getTop(true)];
23572         }
23573         return this.xy || this.el.getXY();
23574     },
23575
23576     /**
23577      * Gets the current box measurements of the component's underlying element.
23578      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23579      * @returns {Object} box An object in the format {x, y, width, height}
23580      */
23581     getBox : function(local){
23582         var s = this.el.getSize();
23583         if(local){
23584             s.x = this.el.getLeft(true);
23585             s.y = this.el.getTop(true);
23586         }else{
23587             var xy = this.xy || this.el.getXY();
23588             s.x = xy[0];
23589             s.y = xy[1];
23590         }
23591         return s;
23592     },
23593
23594     /**
23595      * Sets the current box measurements of the component's underlying element.
23596      * @param {Object} box An object in the format {x, y, width, height}
23597      * @returns {Roo.BoxComponent} this
23598      */
23599     updateBox : function(box){
23600         this.setSize(box.width, box.height);
23601         this.setPagePosition(box.x, box.y);
23602         return this;
23603     },
23604
23605     // protected
23606     getResizeEl : function(){
23607         return this.resizeEl || this.el;
23608     },
23609
23610     // protected
23611     getPositionEl : function(){
23612         return this.positionEl || this.el;
23613     },
23614
23615     /**
23616      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23617      * This method fires the move event.
23618      * @param {Number} left The new left
23619      * @param {Number} top The new top
23620      * @returns {Roo.BoxComponent} this
23621      */
23622     setPosition : function(x, y){
23623         this.x = x;
23624         this.y = y;
23625         if(!this.boxReady){
23626             return this;
23627         }
23628         var adj = this.adjustPosition(x, y);
23629         var ax = adj.x, ay = adj.y;
23630
23631         var el = this.getPositionEl();
23632         if(ax !== undefined || ay !== undefined){
23633             if(ax !== undefined && ay !== undefined){
23634                 el.setLeftTop(ax, ay);
23635             }else if(ax !== undefined){
23636                 el.setLeft(ax);
23637             }else if(ay !== undefined){
23638                 el.setTop(ay);
23639             }
23640             this.onPosition(ax, ay);
23641             this.fireEvent('move', this, ax, ay);
23642         }
23643         return this;
23644     },
23645
23646     /**
23647      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23648      * This method fires the move event.
23649      * @param {Number} x The new x position
23650      * @param {Number} y The new y position
23651      * @returns {Roo.BoxComponent} this
23652      */
23653     setPagePosition : function(x, y){
23654         this.pageX = x;
23655         this.pageY = y;
23656         if(!this.boxReady){
23657             return;
23658         }
23659         if(x === undefined || y === undefined){ // cannot translate undefined points
23660             return;
23661         }
23662         var p = this.el.translatePoints(x, y);
23663         this.setPosition(p.left, p.top);
23664         return this;
23665     },
23666
23667     // private
23668     onRender : function(ct, position){
23669         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23670         if(this.resizeEl){
23671             this.resizeEl = Roo.get(this.resizeEl);
23672         }
23673         if(this.positionEl){
23674             this.positionEl = Roo.get(this.positionEl);
23675         }
23676     },
23677
23678     // private
23679     afterRender : function(){
23680         Roo.BoxComponent.superclass.afterRender.call(this);
23681         this.boxReady = true;
23682         this.setSize(this.width, this.height);
23683         if(this.x || this.y){
23684             this.setPosition(this.x, this.y);
23685         }
23686         if(this.pageX || this.pageY){
23687             this.setPagePosition(this.pageX, this.pageY);
23688         }
23689     },
23690
23691     /**
23692      * Force the component's size to recalculate based on the underlying element's current height and width.
23693      * @returns {Roo.BoxComponent} this
23694      */
23695     syncSize : function(){
23696         delete this.lastSize;
23697         this.setSize(this.el.getWidth(), this.el.getHeight());
23698         return this;
23699     },
23700
23701     /**
23702      * Called after the component is resized, this method is empty by default but can be implemented by any
23703      * subclass that needs to perform custom logic after a resize occurs.
23704      * @param {Number} adjWidth The box-adjusted width that was set
23705      * @param {Number} adjHeight The box-adjusted height that was set
23706      * @param {Number} rawWidth The width that was originally specified
23707      * @param {Number} rawHeight The height that was originally specified
23708      */
23709     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23710
23711     },
23712
23713     /**
23714      * Called after the component is moved, this method is empty by default but can be implemented by any
23715      * subclass that needs to perform custom logic after a move occurs.
23716      * @param {Number} x The new x position
23717      * @param {Number} y The new y position
23718      */
23719     onPosition : function(x, y){
23720
23721     },
23722
23723     // private
23724     adjustSize : function(w, h){
23725         if(this.autoWidth){
23726             w = 'auto';
23727         }
23728         if(this.autoHeight){
23729             h = 'auto';
23730         }
23731         return {width : w, height: h};
23732     },
23733
23734     // private
23735     adjustPosition : function(x, y){
23736         return {x : x, y: y};
23737     }
23738 });/*
23739  * Based on:
23740  * Ext JS Library 1.1.1
23741  * Copyright(c) 2006-2007, Ext JS, LLC.
23742  *
23743  * Originally Released Under LGPL - original licence link has changed is not relivant.
23744  *
23745  * Fork - LGPL
23746  * <script type="text/javascript">
23747  */
23748
23749
23750 /**
23751  * @class Roo.SplitBar
23752  * @extends Roo.util.Observable
23753  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23754  * <br><br>
23755  * Usage:
23756  * <pre><code>
23757 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23758                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23759 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23760 split.minSize = 100;
23761 split.maxSize = 600;
23762 split.animate = true;
23763 split.on('moved', splitterMoved);
23764 </code></pre>
23765  * @constructor
23766  * Create a new SplitBar
23767  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23768  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23769  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23770  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23771                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23772                         position of the SplitBar).
23773  */
23774 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23775     
23776     /** @private */
23777     this.el = Roo.get(dragElement, true);
23778     this.el.dom.unselectable = "on";
23779     /** @private */
23780     this.resizingEl = Roo.get(resizingElement, true);
23781
23782     /**
23783      * @private
23784      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23785      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23786      * @type Number
23787      */
23788     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23789     
23790     /**
23791      * The minimum size of the resizing element. (Defaults to 0)
23792      * @type Number
23793      */
23794     this.minSize = 0;
23795     
23796     /**
23797      * The maximum size of the resizing element. (Defaults to 2000)
23798      * @type Number
23799      */
23800     this.maxSize = 2000;
23801     
23802     /**
23803      * Whether to animate the transition to the new size
23804      * @type Boolean
23805      */
23806     this.animate = false;
23807     
23808     /**
23809      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23810      * @type Boolean
23811      */
23812     this.useShim = false;
23813     
23814     /** @private */
23815     this.shim = null;
23816     
23817     if(!existingProxy){
23818         /** @private */
23819         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23820     }else{
23821         this.proxy = Roo.get(existingProxy).dom;
23822     }
23823     /** @private */
23824     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23825     
23826     /** @private */
23827     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23828     
23829     /** @private */
23830     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23831     
23832     /** @private */
23833     this.dragSpecs = {};
23834     
23835     /**
23836      * @private The adapter to use to positon and resize elements
23837      */
23838     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23839     this.adapter.init(this);
23840     
23841     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23842         /** @private */
23843         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23844         this.el.addClass("x-splitbar-h");
23845     }else{
23846         /** @private */
23847         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23848         this.el.addClass("x-splitbar-v");
23849     }
23850     
23851     this.addEvents({
23852         /**
23853          * @event resize
23854          * Fires when the splitter is moved (alias for {@link #event-moved})
23855          * @param {Roo.SplitBar} this
23856          * @param {Number} newSize the new width or height
23857          */
23858         "resize" : true,
23859         /**
23860          * @event moved
23861          * Fires when the splitter is moved
23862          * @param {Roo.SplitBar} this
23863          * @param {Number} newSize the new width or height
23864          */
23865         "moved" : true,
23866         /**
23867          * @event beforeresize
23868          * Fires before the splitter is dragged
23869          * @param {Roo.SplitBar} this
23870          */
23871         "beforeresize" : true,
23872
23873         "beforeapply" : true
23874     });
23875
23876     Roo.util.Observable.call(this);
23877 };
23878
23879 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23880     onStartProxyDrag : function(x, y){
23881         this.fireEvent("beforeresize", this);
23882         if(!this.overlay){
23883             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23884             o.unselectable();
23885             o.enableDisplayMode("block");
23886             // all splitbars share the same overlay
23887             Roo.SplitBar.prototype.overlay = o;
23888         }
23889         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23890         this.overlay.show();
23891         Roo.get(this.proxy).setDisplayed("block");
23892         var size = this.adapter.getElementSize(this);
23893         this.activeMinSize = this.getMinimumSize();;
23894         this.activeMaxSize = this.getMaximumSize();;
23895         var c1 = size - this.activeMinSize;
23896         var c2 = Math.max(this.activeMaxSize - size, 0);
23897         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23898             this.dd.resetConstraints();
23899             this.dd.setXConstraint(
23900                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23901                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23902             );
23903             this.dd.setYConstraint(0, 0);
23904         }else{
23905             this.dd.resetConstraints();
23906             this.dd.setXConstraint(0, 0);
23907             this.dd.setYConstraint(
23908                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23909                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23910             );
23911          }
23912         this.dragSpecs.startSize = size;
23913         this.dragSpecs.startPoint = [x, y];
23914         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23915     },
23916     
23917     /** 
23918      * @private Called after the drag operation by the DDProxy
23919      */
23920     onEndProxyDrag : function(e){
23921         Roo.get(this.proxy).setDisplayed(false);
23922         var endPoint = Roo.lib.Event.getXY(e);
23923         if(this.overlay){
23924             this.overlay.hide();
23925         }
23926         var newSize;
23927         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23928             newSize = this.dragSpecs.startSize + 
23929                 (this.placement == Roo.SplitBar.LEFT ?
23930                     endPoint[0] - this.dragSpecs.startPoint[0] :
23931                     this.dragSpecs.startPoint[0] - endPoint[0]
23932                 );
23933         }else{
23934             newSize = this.dragSpecs.startSize + 
23935                 (this.placement == Roo.SplitBar.TOP ?
23936                     endPoint[1] - this.dragSpecs.startPoint[1] :
23937                     this.dragSpecs.startPoint[1] - endPoint[1]
23938                 );
23939         }
23940         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23941         if(newSize != this.dragSpecs.startSize){
23942             if(this.fireEvent('beforeapply', this, newSize) !== false){
23943                 this.adapter.setElementSize(this, newSize);
23944                 this.fireEvent("moved", this, newSize);
23945                 this.fireEvent("resize", this, newSize);
23946             }
23947         }
23948     },
23949     
23950     /**
23951      * Get the adapter this SplitBar uses
23952      * @return The adapter object
23953      */
23954     getAdapter : function(){
23955         return this.adapter;
23956     },
23957     
23958     /**
23959      * Set the adapter this SplitBar uses
23960      * @param {Object} adapter A SplitBar adapter object
23961      */
23962     setAdapter : function(adapter){
23963         this.adapter = adapter;
23964         this.adapter.init(this);
23965     },
23966     
23967     /**
23968      * Gets the minimum size for the resizing element
23969      * @return {Number} The minimum size
23970      */
23971     getMinimumSize : function(){
23972         return this.minSize;
23973     },
23974     
23975     /**
23976      * Sets the minimum size for the resizing element
23977      * @param {Number} minSize The minimum size
23978      */
23979     setMinimumSize : function(minSize){
23980         this.minSize = minSize;
23981     },
23982     
23983     /**
23984      * Gets the maximum size for the resizing element
23985      * @return {Number} The maximum size
23986      */
23987     getMaximumSize : function(){
23988         return this.maxSize;
23989     },
23990     
23991     /**
23992      * Sets the maximum size for the resizing element
23993      * @param {Number} maxSize The maximum size
23994      */
23995     setMaximumSize : function(maxSize){
23996         this.maxSize = maxSize;
23997     },
23998     
23999     /**
24000      * Sets the initialize size for the resizing element
24001      * @param {Number} size The initial size
24002      */
24003     setCurrentSize : function(size){
24004         var oldAnimate = this.animate;
24005         this.animate = false;
24006         this.adapter.setElementSize(this, size);
24007         this.animate = oldAnimate;
24008     },
24009     
24010     /**
24011      * Destroy this splitbar. 
24012      * @param {Boolean} removeEl True to remove the element
24013      */
24014     destroy : function(removeEl){
24015         if(this.shim){
24016             this.shim.remove();
24017         }
24018         this.dd.unreg();
24019         this.proxy.parentNode.removeChild(this.proxy);
24020         if(removeEl){
24021             this.el.remove();
24022         }
24023     }
24024 });
24025
24026 /**
24027  * @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.
24028  */
24029 Roo.SplitBar.createProxy = function(dir){
24030     var proxy = new Roo.Element(document.createElement("div"));
24031     proxy.unselectable();
24032     var cls = 'x-splitbar-proxy';
24033     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24034     document.body.appendChild(proxy.dom);
24035     return proxy.dom;
24036 };
24037
24038 /** 
24039  * @class Roo.SplitBar.BasicLayoutAdapter
24040  * Default Adapter. It assumes the splitter and resizing element are not positioned
24041  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24042  */
24043 Roo.SplitBar.BasicLayoutAdapter = function(){
24044 };
24045
24046 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24047     // do nothing for now
24048     init : function(s){
24049     
24050     },
24051     /**
24052      * Called before drag operations to get the current size of the resizing element. 
24053      * @param {Roo.SplitBar} s The SplitBar using this adapter
24054      */
24055      getElementSize : function(s){
24056         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24057             return s.resizingEl.getWidth();
24058         }else{
24059             return s.resizingEl.getHeight();
24060         }
24061     },
24062     
24063     /**
24064      * Called after drag operations to set the size of the resizing element.
24065      * @param {Roo.SplitBar} s The SplitBar using this adapter
24066      * @param {Number} newSize The new size to set
24067      * @param {Function} onComplete A function to be invoked when resizing is complete
24068      */
24069     setElementSize : function(s, newSize, onComplete){
24070         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24071             if(!s.animate){
24072                 s.resizingEl.setWidth(newSize);
24073                 if(onComplete){
24074                     onComplete(s, newSize);
24075                 }
24076             }else{
24077                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24078             }
24079         }else{
24080             
24081             if(!s.animate){
24082                 s.resizingEl.setHeight(newSize);
24083                 if(onComplete){
24084                     onComplete(s, newSize);
24085                 }
24086             }else{
24087                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24088             }
24089         }
24090     }
24091 };
24092
24093 /** 
24094  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24095  * @extends Roo.SplitBar.BasicLayoutAdapter
24096  * Adapter that  moves the splitter element to align with the resized sizing element. 
24097  * Used with an absolute positioned SplitBar.
24098  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24099  * document.body, make sure you assign an id to the body element.
24100  */
24101 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24102     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24103     this.container = Roo.get(container);
24104 };
24105
24106 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24107     init : function(s){
24108         this.basic.init(s);
24109     },
24110     
24111     getElementSize : function(s){
24112         return this.basic.getElementSize(s);
24113     },
24114     
24115     setElementSize : function(s, newSize, onComplete){
24116         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24117     },
24118     
24119     moveSplitter : function(s){
24120         var yes = Roo.SplitBar;
24121         switch(s.placement){
24122             case yes.LEFT:
24123                 s.el.setX(s.resizingEl.getRight());
24124                 break;
24125             case yes.RIGHT:
24126                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24127                 break;
24128             case yes.TOP:
24129                 s.el.setY(s.resizingEl.getBottom());
24130                 break;
24131             case yes.BOTTOM:
24132                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24133                 break;
24134         }
24135     }
24136 };
24137
24138 /**
24139  * Orientation constant - Create a vertical SplitBar
24140  * @static
24141  * @type Number
24142  */
24143 Roo.SplitBar.VERTICAL = 1;
24144
24145 /**
24146  * Orientation constant - Create a horizontal SplitBar
24147  * @static
24148  * @type Number
24149  */
24150 Roo.SplitBar.HORIZONTAL = 2;
24151
24152 /**
24153  * Placement constant - The resizing element is to the left of the splitter element
24154  * @static
24155  * @type Number
24156  */
24157 Roo.SplitBar.LEFT = 1;
24158
24159 /**
24160  * Placement constant - The resizing element is to the right of the splitter element
24161  * @static
24162  * @type Number
24163  */
24164 Roo.SplitBar.RIGHT = 2;
24165
24166 /**
24167  * Placement constant - The resizing element is positioned above the splitter element
24168  * @static
24169  * @type Number
24170  */
24171 Roo.SplitBar.TOP = 3;
24172
24173 /**
24174  * Placement constant - The resizing element is positioned under splitter element
24175  * @static
24176  * @type Number
24177  */
24178 Roo.SplitBar.BOTTOM = 4;
24179 /*
24180  * Based on:
24181  * Ext JS Library 1.1.1
24182  * Copyright(c) 2006-2007, Ext JS, LLC.
24183  *
24184  * Originally Released Under LGPL - original licence link has changed is not relivant.
24185  *
24186  * Fork - LGPL
24187  * <script type="text/javascript">
24188  */
24189
24190 /**
24191  * @class Roo.View
24192  * @extends Roo.util.Observable
24193  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24194  * This class also supports single and multi selection modes. <br>
24195  * Create a data model bound view:
24196  <pre><code>
24197  var store = new Roo.data.Store(...);
24198
24199  var view = new Roo.View({
24200     el : "my-element",
24201     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24202  
24203     singleSelect: true,
24204     selectedClass: "ydataview-selected",
24205     store: store
24206  });
24207
24208  // listen for node click?
24209  view.on("click", function(vw, index, node, e){
24210  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24211  });
24212
24213  // load XML data
24214  dataModel.load("foobar.xml");
24215  </code></pre>
24216  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24217  * <br><br>
24218  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24219  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24220  * 
24221  * Note: old style constructor is still suported (container, template, config)
24222  * 
24223  * @constructor
24224  * Create a new View
24225  * @param {Object} config The config object
24226  * 
24227  */
24228 Roo.View = function(config, depreciated_tpl, depreciated_config){
24229     
24230     if (typeof(depreciated_tpl) == 'undefined') {
24231         // new way.. - universal constructor.
24232         Roo.apply(this, config);
24233         this.el  = Roo.get(this.el);
24234     } else {
24235         // old format..
24236         this.el  = Roo.get(config);
24237         this.tpl = depreciated_tpl;
24238         Roo.apply(this, depreciated_config);
24239     }
24240     this.wrapEl  = this.el.wrap().wrap();
24241     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24242     
24243     
24244     if(typeof(this.tpl) == "string"){
24245         this.tpl = new Roo.Template(this.tpl);
24246     } else {
24247         // support xtype ctors..
24248         this.tpl = new Roo.factory(this.tpl, Roo);
24249     }
24250     
24251     
24252     this.tpl.compile();
24253    
24254   
24255     
24256      
24257     /** @private */
24258     this.addEvents({
24259         /**
24260          * @event beforeclick
24261          * Fires before a click is processed. Returns false to cancel the default action.
24262          * @param {Roo.View} this
24263          * @param {Number} index The index of the target node
24264          * @param {HTMLElement} node The target node
24265          * @param {Roo.EventObject} e The raw event object
24266          */
24267             "beforeclick" : true,
24268         /**
24269          * @event click
24270          * Fires when a template node is clicked.
24271          * @param {Roo.View} this
24272          * @param {Number} index The index of the target node
24273          * @param {HTMLElement} node The target node
24274          * @param {Roo.EventObject} e The raw event object
24275          */
24276             "click" : true,
24277         /**
24278          * @event dblclick
24279          * Fires when a template node is double clicked.
24280          * @param {Roo.View} this
24281          * @param {Number} index The index of the target node
24282          * @param {HTMLElement} node The target node
24283          * @param {Roo.EventObject} e The raw event object
24284          */
24285             "dblclick" : true,
24286         /**
24287          * @event contextmenu
24288          * Fires when a template node is right clicked.
24289          * @param {Roo.View} this
24290          * @param {Number} index The index of the target node
24291          * @param {HTMLElement} node The target node
24292          * @param {Roo.EventObject} e The raw event object
24293          */
24294             "contextmenu" : true,
24295         /**
24296          * @event selectionchange
24297          * Fires when the selected nodes change.
24298          * @param {Roo.View} this
24299          * @param {Array} selections Array of the selected nodes
24300          */
24301             "selectionchange" : true,
24302     
24303         /**
24304          * @event beforeselect
24305          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24306          * @param {Roo.View} this
24307          * @param {HTMLElement} node The node to be selected
24308          * @param {Array} selections Array of currently selected nodes
24309          */
24310             "beforeselect" : true,
24311         /**
24312          * @event preparedata
24313          * Fires on every row to render, to allow you to change the data.
24314          * @param {Roo.View} this
24315          * @param {Object} data to be rendered (change this)
24316          */
24317           "preparedata" : true
24318           
24319           
24320         });
24321
24322
24323
24324     this.el.on({
24325         "click": this.onClick,
24326         "dblclick": this.onDblClick,
24327         "contextmenu": this.onContextMenu,
24328         scope:this
24329     });
24330
24331     this.selections = [];
24332     this.nodes = [];
24333     this.cmp = new Roo.CompositeElementLite([]);
24334     if(this.store){
24335         this.store = Roo.factory(this.store, Roo.data);
24336         this.setStore(this.store, true);
24337     }
24338     
24339     if ( this.footer && this.footer.xtype) {
24340            
24341          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24342         
24343         this.footer.dataSource = this.store
24344         this.footer.container = fctr;
24345         this.footer = Roo.factory(this.footer, Roo);
24346         fctr.insertFirst(this.el);
24347         
24348         // this is a bit insane - as the paging toolbar seems to detach the el..
24349 //        dom.parentNode.parentNode.parentNode
24350          // they get detached?
24351     }
24352     
24353     
24354     Roo.View.superclass.constructor.call(this);
24355     
24356     
24357 };
24358
24359 Roo.extend(Roo.View, Roo.util.Observable, {
24360     
24361      /**
24362      * @cfg {Roo.data.Store} store Data store to load data from.
24363      */
24364     store : false,
24365     
24366     /**
24367      * @cfg {String|Roo.Element} el The container element.
24368      */
24369     el : '',
24370     
24371     /**
24372      * @cfg {String|Roo.Template} tpl The template used by this View 
24373      */
24374     tpl : false,
24375     /**
24376      * @cfg {String} dataName the named area of the template to use as the data area
24377      *                          Works with domtemplates roo-name="name"
24378      */
24379     dataName: false,
24380     /**
24381      * @cfg {String} selectedClass The css class to add to selected nodes
24382      */
24383     selectedClass : "x-view-selected",
24384      /**
24385      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24386      */
24387     emptyText : "",
24388     
24389     /**
24390      * @cfg {String} text to display on mask (default Loading)
24391      */
24392     mask : false,
24393     /**
24394      * @cfg {Boolean} multiSelect Allow multiple selection
24395      */
24396     multiSelect : false,
24397     /**
24398      * @cfg {Boolean} singleSelect Allow single selection
24399      */
24400     singleSelect:  false,
24401     
24402     /**
24403      * @cfg {Boolean} toggleSelect - selecting 
24404      */
24405     toggleSelect : false,
24406     
24407     /**
24408      * Returns the element this view is bound to.
24409      * @return {Roo.Element}
24410      */
24411     getEl : function(){
24412         return this.wrapEl;
24413     },
24414     
24415     
24416
24417     /**
24418      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24419      */
24420     refresh : function(){
24421         var t = this.tpl;
24422         
24423         // if we are using something like 'domtemplate', then
24424         // the what gets used is:
24425         // t.applySubtemplate(NAME, data, wrapping data..)
24426         // the outer template then get' applied with
24427         //     the store 'extra data'
24428         // and the body get's added to the
24429         //      roo-name="data" node?
24430         //      <span class='roo-tpl-{name}'></span> ?????
24431         
24432         
24433         
24434         this.clearSelections();
24435         this.el.update("");
24436         var html = [];
24437         var records = this.store.getRange();
24438         if(records.length < 1) {
24439             
24440             // is this valid??  = should it render a template??
24441             
24442             this.el.update(this.emptyText);
24443             return;
24444         }
24445         var el = this.el;
24446         if (this.dataName) {
24447             this.el.update(t.apply(this.store.meta)); //????
24448             el = this.el.child('.roo-tpl-' + this.dataName);
24449         }
24450         
24451         for(var i = 0, len = records.length; i < len; i++){
24452             var data = this.prepareData(records[i].data, i, records[i]);
24453             this.fireEvent("preparedata", this, data, i, records[i]);
24454             html[html.length] = Roo.util.Format.trim(
24455                 this.dataName ?
24456                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24457                     t.apply(data)
24458             );
24459         }
24460         
24461         
24462         
24463         el.update(html.join(""));
24464         this.nodes = el.dom.childNodes;
24465         this.updateIndexes(0);
24466     },
24467
24468     /**
24469      * Function to override to reformat the data that is sent to
24470      * the template for each node.
24471      * DEPRICATED - use the preparedata event handler.
24472      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24473      * a JSON object for an UpdateManager bound view).
24474      */
24475     prepareData : function(data, index, record)
24476     {
24477         this.fireEvent("preparedata", this, data, index, record);
24478         return data;
24479     },
24480
24481     onUpdate : function(ds, record){
24482         this.clearSelections();
24483         var index = this.store.indexOf(record);
24484         var n = this.nodes[index];
24485         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24486         n.parentNode.removeChild(n);
24487         this.updateIndexes(index, index);
24488     },
24489
24490     
24491     
24492 // --------- FIXME     
24493     onAdd : function(ds, records, index)
24494     {
24495         this.clearSelections();
24496         if(this.nodes.length == 0){
24497             this.refresh();
24498             return;
24499         }
24500         var n = this.nodes[index];
24501         for(var i = 0, len = records.length; i < len; i++){
24502             var d = this.prepareData(records[i].data, i, records[i]);
24503             if(n){
24504                 this.tpl.insertBefore(n, d);
24505             }else{
24506                 
24507                 this.tpl.append(this.el, d);
24508             }
24509         }
24510         this.updateIndexes(index);
24511     },
24512
24513     onRemove : function(ds, record, index){
24514         this.clearSelections();
24515         var el = this.dataName  ?
24516             this.el.child('.roo-tpl-' + this.dataName) :
24517             this.el; 
24518         el.dom.removeChild(this.nodes[index]);
24519         this.updateIndexes(index);
24520     },
24521
24522     /**
24523      * Refresh an individual node.
24524      * @param {Number} index
24525      */
24526     refreshNode : function(index){
24527         this.onUpdate(this.store, this.store.getAt(index));
24528     },
24529
24530     updateIndexes : function(startIndex, endIndex){
24531         var ns = this.nodes;
24532         startIndex = startIndex || 0;
24533         endIndex = endIndex || ns.length - 1;
24534         for(var i = startIndex; i <= endIndex; i++){
24535             ns[i].nodeIndex = i;
24536         }
24537     },
24538
24539     /**
24540      * Changes the data store this view uses and refresh the view.
24541      * @param {Store} store
24542      */
24543     setStore : function(store, initial){
24544         if(!initial && this.store){
24545             this.store.un("datachanged", this.refresh);
24546             this.store.un("add", this.onAdd);
24547             this.store.un("remove", this.onRemove);
24548             this.store.un("update", this.onUpdate);
24549             this.store.un("clear", this.refresh);
24550             this.store.un("beforeload", this.onBeforeLoad);
24551             this.store.un("load", this.onLoad);
24552             this.store.un("loadexception", this.onLoad);
24553         }
24554         if(store){
24555           
24556             store.on("datachanged", this.refresh, this);
24557             store.on("add", this.onAdd, this);
24558             store.on("remove", this.onRemove, this);
24559             store.on("update", this.onUpdate, this);
24560             store.on("clear", this.refresh, this);
24561             store.on("beforeload", this.onBeforeLoad, this);
24562             store.on("load", this.onLoad, this);
24563             store.on("loadexception", this.onLoad, this);
24564         }
24565         
24566         if(store){
24567             this.refresh();
24568         }
24569     },
24570     /**
24571      * onbeforeLoad - masks the loading area.
24572      *
24573      */
24574     onBeforeLoad : function()
24575     {
24576         this.el.update("");
24577         this.el.mask(this.mask ? this.mask : "Loading" ); 
24578     },
24579     onLoad : function ()
24580     {
24581         this.el.unmask();
24582     },
24583     
24584
24585     /**
24586      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
24587      * @param {HTMLElement} node
24588      * @return {HTMLElement} The template node
24589      */
24590     findItemFromChild : function(node){
24591         var el = this.dataName  ?
24592             this.el.child('.roo-tpl-' + this.dataName,true) :
24593             this.el.dom; 
24594         
24595         if(!node || node.parentNode == el){
24596                     return node;
24597             }
24598             var p = node.parentNode;
24599             while(p && p != el){
24600             if(p.parentNode == el){
24601                 return p;
24602             }
24603             p = p.parentNode;
24604         }
24605             return null;
24606     },
24607
24608     /** @ignore */
24609     onClick : function(e){
24610         var item = this.findItemFromChild(e.getTarget());
24611         if(item){
24612             var index = this.indexOf(item);
24613             if(this.onItemClick(item, index, e) !== false){
24614                 this.fireEvent("click", this, index, item, e);
24615             }
24616         }else{
24617             this.clearSelections();
24618         }
24619     },
24620
24621     /** @ignore */
24622     onContextMenu : function(e){
24623         var item = this.findItemFromChild(e.getTarget());
24624         if(item){
24625             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
24626         }
24627     },
24628
24629     /** @ignore */
24630     onDblClick : function(e){
24631         var item = this.findItemFromChild(e.getTarget());
24632         if(item){
24633             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
24634         }
24635     },
24636
24637     onItemClick : function(item, index, e)
24638     {
24639         if(this.fireEvent("beforeclick", this, index, item, e) === false){
24640             return false;
24641         }
24642         if (this.toggleSelect) {
24643             var m = this.isSelected(item) ? 'unselect' : 'select';
24644             Roo.log(m);
24645             var _t = this;
24646             _t[m](item, true, false);
24647             return true;
24648         }
24649         if(this.multiSelect || this.singleSelect){
24650             if(this.multiSelect && e.shiftKey && this.lastSelection){
24651                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
24652             }else{
24653                 this.select(item, this.multiSelect && e.ctrlKey);
24654                 this.lastSelection = item;
24655             }
24656             e.preventDefault();
24657         }
24658         return true;
24659     },
24660
24661     /**
24662      * Get the number of selected nodes.
24663      * @return {Number}
24664      */
24665     getSelectionCount : function(){
24666         return this.selections.length;
24667     },
24668
24669     /**
24670      * Get the currently selected nodes.
24671      * @return {Array} An array of HTMLElements
24672      */
24673     getSelectedNodes : function(){
24674         return this.selections;
24675     },
24676
24677     /**
24678      * Get the indexes of the selected nodes.
24679      * @return {Array}
24680      */
24681     getSelectedIndexes : function(){
24682         var indexes = [], s = this.selections;
24683         for(var i = 0, len = s.length; i < len; i++){
24684             indexes.push(s[i].nodeIndex);
24685         }
24686         return indexes;
24687     },
24688
24689     /**
24690      * Clear all selections
24691      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
24692      */
24693     clearSelections : function(suppressEvent){
24694         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24695             this.cmp.elements = this.selections;
24696             this.cmp.removeClass(this.selectedClass);
24697             this.selections = [];
24698             if(!suppressEvent){
24699                 this.fireEvent("selectionchange", this, this.selections);
24700             }
24701         }
24702     },
24703
24704     /**
24705      * Returns true if the passed node is selected
24706      * @param {HTMLElement/Number} node The node or node index
24707      * @return {Boolean}
24708      */
24709     isSelected : function(node){
24710         var s = this.selections;
24711         if(s.length < 1){
24712             return false;
24713         }
24714         node = this.getNode(node);
24715         return s.indexOf(node) !== -1;
24716     },
24717
24718     /**
24719      * Selects nodes.
24720      * @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
24721      * @param {Boolean} keepExisting (optional) true to keep existing selections
24722      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24723      */
24724     select : function(nodeInfo, keepExisting, suppressEvent){
24725         if(nodeInfo instanceof Array){
24726             if(!keepExisting){
24727                 this.clearSelections(true);
24728             }
24729             for(var i = 0, len = nodeInfo.length; i < len; i++){
24730                 this.select(nodeInfo[i], true, true);
24731             }
24732             return;
24733         } 
24734         var node = this.getNode(nodeInfo);
24735         if(!node || this.isSelected(node)){
24736             return; // already selected.
24737         }
24738         if(!keepExisting){
24739             this.clearSelections(true);
24740         }
24741         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24742             Roo.fly(node).addClass(this.selectedClass);
24743             this.selections.push(node);
24744             if(!suppressEvent){
24745                 this.fireEvent("selectionchange", this, this.selections);
24746             }
24747         }
24748         
24749         
24750     },
24751       /**
24752      * Unselects nodes.
24753      * @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
24754      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24755      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24756      */
24757     unselect : function(nodeInfo, keepExisting, suppressEvent)
24758     {
24759         if(nodeInfo instanceof Array){
24760             Roo.each(this.selections, function(s) {
24761                 this.unselect(s, nodeInfo);
24762             }, this);
24763             return;
24764         }
24765         var node = this.getNode(nodeInfo);
24766         if(!node || !this.isSelected(node)){
24767             Roo.log("not selected");
24768             return; // not selected.
24769         }
24770         // fireevent???
24771         var ns = [];
24772         Roo.each(this.selections, function(s) {
24773             if (s == node ) {
24774                 Roo.fly(node).removeClass(this.selectedClass);
24775
24776                 return;
24777             }
24778             ns.push(s);
24779         },this);
24780         
24781         this.selections= ns;
24782         this.fireEvent("selectionchange", this, this.selections);
24783     },
24784
24785     /**
24786      * Gets a template node.
24787      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24788      * @return {HTMLElement} The node or null if it wasn't found
24789      */
24790     getNode : function(nodeInfo){
24791         if(typeof nodeInfo == "string"){
24792             return document.getElementById(nodeInfo);
24793         }else if(typeof nodeInfo == "number"){
24794             return this.nodes[nodeInfo];
24795         }
24796         return nodeInfo;
24797     },
24798
24799     /**
24800      * Gets a range template nodes.
24801      * @param {Number} startIndex
24802      * @param {Number} endIndex
24803      * @return {Array} An array of nodes
24804      */
24805     getNodes : function(start, end){
24806         var ns = this.nodes;
24807         start = start || 0;
24808         end = typeof end == "undefined" ? ns.length - 1 : end;
24809         var nodes = [];
24810         if(start <= end){
24811             for(var i = start; i <= end; i++){
24812                 nodes.push(ns[i]);
24813             }
24814         } else{
24815             for(var i = start; i >= end; i--){
24816                 nodes.push(ns[i]);
24817             }
24818         }
24819         return nodes;
24820     },
24821
24822     /**
24823      * Finds the index of the passed node
24824      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24825      * @return {Number} The index of the node or -1
24826      */
24827     indexOf : function(node){
24828         node = this.getNode(node);
24829         if(typeof node.nodeIndex == "number"){
24830             return node.nodeIndex;
24831         }
24832         var ns = this.nodes;
24833         for(var i = 0, len = ns.length; i < len; i++){
24834             if(ns[i] == node){
24835                 return i;
24836             }
24837         }
24838         return -1;
24839     }
24840 });
24841 /*
24842  * Based on:
24843  * Ext JS Library 1.1.1
24844  * Copyright(c) 2006-2007, Ext JS, LLC.
24845  *
24846  * Originally Released Under LGPL - original licence link has changed is not relivant.
24847  *
24848  * Fork - LGPL
24849  * <script type="text/javascript">
24850  */
24851
24852 /**
24853  * @class Roo.JsonView
24854  * @extends Roo.View
24855  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24856 <pre><code>
24857 var view = new Roo.JsonView({
24858     container: "my-element",
24859     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24860     multiSelect: true, 
24861     jsonRoot: "data" 
24862 });
24863
24864 // listen for node click?
24865 view.on("click", function(vw, index, node, e){
24866     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24867 });
24868
24869 // direct load of JSON data
24870 view.load("foobar.php");
24871
24872 // Example from my blog list
24873 var tpl = new Roo.Template(
24874     '&lt;div class="entry"&gt;' +
24875     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24876     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24877     "&lt;/div&gt;&lt;hr /&gt;"
24878 );
24879
24880 var moreView = new Roo.JsonView({
24881     container :  "entry-list", 
24882     template : tpl,
24883     jsonRoot: "posts"
24884 });
24885 moreView.on("beforerender", this.sortEntries, this);
24886 moreView.load({
24887     url: "/blog/get-posts.php",
24888     params: "allposts=true",
24889     text: "Loading Blog Entries..."
24890 });
24891 </code></pre>
24892
24893 * Note: old code is supported with arguments : (container, template, config)
24894
24895
24896  * @constructor
24897  * Create a new JsonView
24898  * 
24899  * @param {Object} config The config object
24900  * 
24901  */
24902 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24903     
24904     
24905     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24906
24907     var um = this.el.getUpdateManager();
24908     um.setRenderer(this);
24909     um.on("update", this.onLoad, this);
24910     um.on("failure", this.onLoadException, this);
24911
24912     /**
24913      * @event beforerender
24914      * Fires before rendering of the downloaded JSON data.
24915      * @param {Roo.JsonView} this
24916      * @param {Object} data The JSON data loaded
24917      */
24918     /**
24919      * @event load
24920      * Fires when data is loaded.
24921      * @param {Roo.JsonView} this
24922      * @param {Object} data The JSON data loaded
24923      * @param {Object} response The raw Connect response object
24924      */
24925     /**
24926      * @event loadexception
24927      * Fires when loading fails.
24928      * @param {Roo.JsonView} this
24929      * @param {Object} response The raw Connect response object
24930      */
24931     this.addEvents({
24932         'beforerender' : true,
24933         'load' : true,
24934         'loadexception' : true
24935     });
24936 };
24937 Roo.extend(Roo.JsonView, Roo.View, {
24938     /**
24939      * @type {String} The root property in the loaded JSON object that contains the data
24940      */
24941     jsonRoot : "",
24942
24943     /**
24944      * Refreshes the view.
24945      */
24946     refresh : function(){
24947         this.clearSelections();
24948         this.el.update("");
24949         var html = [];
24950         var o = this.jsonData;
24951         if(o && o.length > 0){
24952             for(var i = 0, len = o.length; i < len; i++){
24953                 var data = this.prepareData(o[i], i, o);
24954                 html[html.length] = this.tpl.apply(data);
24955             }
24956         }else{
24957             html.push(this.emptyText);
24958         }
24959         this.el.update(html.join(""));
24960         this.nodes = this.el.dom.childNodes;
24961         this.updateIndexes(0);
24962     },
24963
24964     /**
24965      * 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.
24966      * @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:
24967      <pre><code>
24968      view.load({
24969          url: "your-url.php",
24970          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24971          callback: yourFunction,
24972          scope: yourObject, //(optional scope)
24973          discardUrl: false,
24974          nocache: false,
24975          text: "Loading...",
24976          timeout: 30,
24977          scripts: false
24978      });
24979      </code></pre>
24980      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24981      * 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.
24982      * @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}
24983      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24984      * @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.
24985      */
24986     load : function(){
24987         var um = this.el.getUpdateManager();
24988         um.update.apply(um, arguments);
24989     },
24990
24991     render : function(el, response){
24992         this.clearSelections();
24993         this.el.update("");
24994         var o;
24995         try{
24996             o = Roo.util.JSON.decode(response.responseText);
24997             if(this.jsonRoot){
24998                 
24999                 o = o[this.jsonRoot];
25000             }
25001         } catch(e){
25002         }
25003         /**
25004          * The current JSON data or null
25005          */
25006         this.jsonData = o;
25007         this.beforeRender();
25008         this.refresh();
25009     },
25010
25011 /**
25012  * Get the number of records in the current JSON dataset
25013  * @return {Number}
25014  */
25015     getCount : function(){
25016         return this.jsonData ? this.jsonData.length : 0;
25017     },
25018
25019 /**
25020  * Returns the JSON object for the specified node(s)
25021  * @param {HTMLElement/Array} node The node or an array of nodes
25022  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25023  * you get the JSON object for the node
25024  */
25025     getNodeData : function(node){
25026         if(node instanceof Array){
25027             var data = [];
25028             for(var i = 0, len = node.length; i < len; i++){
25029                 data.push(this.getNodeData(node[i]));
25030             }
25031             return data;
25032         }
25033         return this.jsonData[this.indexOf(node)] || null;
25034     },
25035
25036     beforeRender : function(){
25037         this.snapshot = this.jsonData;
25038         if(this.sortInfo){
25039             this.sort.apply(this, this.sortInfo);
25040         }
25041         this.fireEvent("beforerender", this, this.jsonData);
25042     },
25043
25044     onLoad : function(el, o){
25045         this.fireEvent("load", this, this.jsonData, o);
25046     },
25047
25048     onLoadException : function(el, o){
25049         this.fireEvent("loadexception", this, o);
25050     },
25051
25052 /**
25053  * Filter the data by a specific property.
25054  * @param {String} property A property on your JSON objects
25055  * @param {String/RegExp} value Either string that the property values
25056  * should start with, or a RegExp to test against the property
25057  */
25058     filter : function(property, value){
25059         if(this.jsonData){
25060             var data = [];
25061             var ss = this.snapshot;
25062             if(typeof value == "string"){
25063                 var vlen = value.length;
25064                 if(vlen == 0){
25065                     this.clearFilter();
25066                     return;
25067                 }
25068                 value = value.toLowerCase();
25069                 for(var i = 0, len = ss.length; i < len; i++){
25070                     var o = ss[i];
25071                     if(o[property].substr(0, vlen).toLowerCase() == value){
25072                         data.push(o);
25073                     }
25074                 }
25075             } else if(value.exec){ // regex?
25076                 for(var i = 0, len = ss.length; i < len; i++){
25077                     var o = ss[i];
25078                     if(value.test(o[property])){
25079                         data.push(o);
25080                     }
25081                 }
25082             } else{
25083                 return;
25084             }
25085             this.jsonData = data;
25086             this.refresh();
25087         }
25088     },
25089
25090 /**
25091  * Filter by a function. The passed function will be called with each
25092  * object in the current dataset. If the function returns true the value is kept,
25093  * otherwise it is filtered.
25094  * @param {Function} fn
25095  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25096  */
25097     filterBy : function(fn, scope){
25098         if(this.jsonData){
25099             var data = [];
25100             var ss = this.snapshot;
25101             for(var i = 0, len = ss.length; i < len; i++){
25102                 var o = ss[i];
25103                 if(fn.call(scope || this, o)){
25104                     data.push(o);
25105                 }
25106             }
25107             this.jsonData = data;
25108             this.refresh();
25109         }
25110     },
25111
25112 /**
25113  * Clears the current filter.
25114  */
25115     clearFilter : function(){
25116         if(this.snapshot && this.jsonData != this.snapshot){
25117             this.jsonData = this.snapshot;
25118             this.refresh();
25119         }
25120     },
25121
25122
25123 /**
25124  * Sorts the data for this view and refreshes it.
25125  * @param {String} property A property on your JSON objects to sort on
25126  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25127  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25128  */
25129     sort : function(property, dir, sortType){
25130         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25131         if(this.jsonData){
25132             var p = property;
25133             var dsc = dir && dir.toLowerCase() == "desc";
25134             var f = function(o1, o2){
25135                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25136                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25137                 ;
25138                 if(v1 < v2){
25139                     return dsc ? +1 : -1;
25140                 } else if(v1 > v2){
25141                     return dsc ? -1 : +1;
25142                 } else{
25143                     return 0;
25144                 }
25145             };
25146             this.jsonData.sort(f);
25147             this.refresh();
25148             if(this.jsonData != this.snapshot){
25149                 this.snapshot.sort(f);
25150             }
25151         }
25152     }
25153 });/*
25154  * Based on:
25155  * Ext JS Library 1.1.1
25156  * Copyright(c) 2006-2007, Ext JS, LLC.
25157  *
25158  * Originally Released Under LGPL - original licence link has changed is not relivant.
25159  *
25160  * Fork - LGPL
25161  * <script type="text/javascript">
25162  */
25163  
25164
25165 /**
25166  * @class Roo.ColorPalette
25167  * @extends Roo.Component
25168  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25169  * Here's an example of typical usage:
25170  * <pre><code>
25171 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25172 cp.render('my-div');
25173
25174 cp.on('select', function(palette, selColor){
25175     // do something with selColor
25176 });
25177 </code></pre>
25178  * @constructor
25179  * Create a new ColorPalette
25180  * @param {Object} config The config object
25181  */
25182 Roo.ColorPalette = function(config){
25183     Roo.ColorPalette.superclass.constructor.call(this, config);
25184     this.addEvents({
25185         /**
25186              * @event select
25187              * Fires when a color is selected
25188              * @param {ColorPalette} this
25189              * @param {String} color The 6-digit color hex code (without the # symbol)
25190              */
25191         select: true
25192     });
25193
25194     if(this.handler){
25195         this.on("select", this.handler, this.scope, true);
25196     }
25197 };
25198 Roo.extend(Roo.ColorPalette, Roo.Component, {
25199     /**
25200      * @cfg {String} itemCls
25201      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25202      */
25203     itemCls : "x-color-palette",
25204     /**
25205      * @cfg {String} value
25206      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25207      * the hex codes are case-sensitive.
25208      */
25209     value : null,
25210     clickEvent:'click',
25211     // private
25212     ctype: "Roo.ColorPalette",
25213
25214     /**
25215      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25216      */
25217     allowReselect : false,
25218
25219     /**
25220      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25221      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25222      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25223      * of colors with the width setting until the box is symmetrical.</p>
25224      * <p>You can override individual colors if needed:</p>
25225      * <pre><code>
25226 var cp = new Roo.ColorPalette();
25227 cp.colors[0] = "FF0000";  // change the first box to red
25228 </code></pre>
25229
25230 Or you can provide a custom array of your own for complete control:
25231 <pre><code>
25232 var cp = new Roo.ColorPalette();
25233 cp.colors = ["000000", "993300", "333300"];
25234 </code></pre>
25235      * @type Array
25236      */
25237     colors : [
25238         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25239         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25240         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25241         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25242         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25243     ],
25244
25245     // private
25246     onRender : function(container, position){
25247         var t = new Roo.MasterTemplate(
25248             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25249         );
25250         var c = this.colors;
25251         for(var i = 0, len = c.length; i < len; i++){
25252             t.add([c[i]]);
25253         }
25254         var el = document.createElement("div");
25255         el.className = this.itemCls;
25256         t.overwrite(el);
25257         container.dom.insertBefore(el, position);
25258         this.el = Roo.get(el);
25259         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25260         if(this.clickEvent != 'click'){
25261             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25262         }
25263     },
25264
25265     // private
25266     afterRender : function(){
25267         Roo.ColorPalette.superclass.afterRender.call(this);
25268         if(this.value){
25269             var s = this.value;
25270             this.value = null;
25271             this.select(s);
25272         }
25273     },
25274
25275     // private
25276     handleClick : function(e, t){
25277         e.preventDefault();
25278         if(!this.disabled){
25279             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25280             this.select(c.toUpperCase());
25281         }
25282     },
25283
25284     /**
25285      * Selects the specified color in the palette (fires the select event)
25286      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25287      */
25288     select : function(color){
25289         color = color.replace("#", "");
25290         if(color != this.value || this.allowReselect){
25291             var el = this.el;
25292             if(this.value){
25293                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25294             }
25295             el.child("a.color-"+color).addClass("x-color-palette-sel");
25296             this.value = color;
25297             this.fireEvent("select", this, color);
25298         }
25299     }
25300 });/*
25301  * Based on:
25302  * Ext JS Library 1.1.1
25303  * Copyright(c) 2006-2007, Ext JS, LLC.
25304  *
25305  * Originally Released Under LGPL - original licence link has changed is not relivant.
25306  *
25307  * Fork - LGPL
25308  * <script type="text/javascript">
25309  */
25310  
25311 /**
25312  * @class Roo.DatePicker
25313  * @extends Roo.Component
25314  * Simple date picker class.
25315  * @constructor
25316  * Create a new DatePicker
25317  * @param {Object} config The config object
25318  */
25319 Roo.DatePicker = function(config){
25320     Roo.DatePicker.superclass.constructor.call(this, config);
25321
25322     this.value = config && config.value ?
25323                  config.value.clearTime() : new Date().clearTime();
25324
25325     this.addEvents({
25326         /**
25327              * @event select
25328              * Fires when a date is selected
25329              * @param {DatePicker} this
25330              * @param {Date} date The selected date
25331              */
25332         'select': true,
25333         /**
25334              * @event monthchange
25335              * Fires when the displayed month changes 
25336              * @param {DatePicker} this
25337              * @param {Date} date The selected month
25338              */
25339         'monthchange': true
25340     });
25341
25342     if(this.handler){
25343         this.on("select", this.handler,  this.scope || this);
25344     }
25345     // build the disabledDatesRE
25346     if(!this.disabledDatesRE && this.disabledDates){
25347         var dd = this.disabledDates;
25348         var re = "(?:";
25349         for(var i = 0; i < dd.length; i++){
25350             re += dd[i];
25351             if(i != dd.length-1) re += "|";
25352         }
25353         this.disabledDatesRE = new RegExp(re + ")");
25354     }
25355 };
25356
25357 Roo.extend(Roo.DatePicker, Roo.Component, {
25358     /**
25359      * @cfg {String} todayText
25360      * The text to display on the button that selects the current date (defaults to "Today")
25361      */
25362     todayText : "Today",
25363     /**
25364      * @cfg {String} okText
25365      * The text to display on the ok button
25366      */
25367     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25368     /**
25369      * @cfg {String} cancelText
25370      * The text to display on the cancel button
25371      */
25372     cancelText : "Cancel",
25373     /**
25374      * @cfg {String} todayTip
25375      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25376      */
25377     todayTip : "{0} (Spacebar)",
25378     /**
25379      * @cfg {Date} minDate
25380      * Minimum allowable date (JavaScript date object, defaults to null)
25381      */
25382     minDate : null,
25383     /**
25384      * @cfg {Date} maxDate
25385      * Maximum allowable date (JavaScript date object, defaults to null)
25386      */
25387     maxDate : null,
25388     /**
25389      * @cfg {String} minText
25390      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25391      */
25392     minText : "This date is before the minimum date",
25393     /**
25394      * @cfg {String} maxText
25395      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25396      */
25397     maxText : "This date is after the maximum date",
25398     /**
25399      * @cfg {String} format
25400      * The default date format string which can be overriden for localization support.  The format must be
25401      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25402      */
25403     format : "m/d/y",
25404     /**
25405      * @cfg {Array} disabledDays
25406      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25407      */
25408     disabledDays : null,
25409     /**
25410      * @cfg {String} disabledDaysText
25411      * The tooltip to display when the date falls on a disabled day (defaults to "")
25412      */
25413     disabledDaysText : "",
25414     /**
25415      * @cfg {RegExp} disabledDatesRE
25416      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25417      */
25418     disabledDatesRE : null,
25419     /**
25420      * @cfg {String} disabledDatesText
25421      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25422      */
25423     disabledDatesText : "",
25424     /**
25425      * @cfg {Boolean} constrainToViewport
25426      * True to constrain the date picker to the viewport (defaults to true)
25427      */
25428     constrainToViewport : true,
25429     /**
25430      * @cfg {Array} monthNames
25431      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25432      */
25433     monthNames : Date.monthNames,
25434     /**
25435      * @cfg {Array} dayNames
25436      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25437      */
25438     dayNames : Date.dayNames,
25439     /**
25440      * @cfg {String} nextText
25441      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25442      */
25443     nextText: 'Next Month (Control+Right)',
25444     /**
25445      * @cfg {String} prevText
25446      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25447      */
25448     prevText: 'Previous Month (Control+Left)',
25449     /**
25450      * @cfg {String} monthYearText
25451      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25452      */
25453     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25454     /**
25455      * @cfg {Number} startDay
25456      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25457      */
25458     startDay : 0,
25459     /**
25460      * @cfg {Bool} showClear
25461      * Show a clear button (usefull for date form elements that can be blank.)
25462      */
25463     
25464     showClear: false,
25465     
25466     /**
25467      * Sets the value of the date field
25468      * @param {Date} value The date to set
25469      */
25470     setValue : function(value){
25471         var old = this.value;
25472         
25473         if (typeof(value) == 'string') {
25474          
25475             value = Date.parseDate(value, this.format);
25476         }
25477         if (!value) {
25478             value = new Date();
25479         }
25480         
25481         this.value = value.clearTime(true);
25482         if(this.el){
25483             this.update(this.value);
25484         }
25485     },
25486
25487     /**
25488      * Gets the current selected value of the date field
25489      * @return {Date} The selected date
25490      */
25491     getValue : function(){
25492         return this.value;
25493     },
25494
25495     // private
25496     focus : function(){
25497         if(this.el){
25498             this.update(this.activeDate);
25499         }
25500     },
25501
25502     // privateval
25503     onRender : function(container, position){
25504         
25505         var m = [
25506              '<table cellspacing="0">',
25507                 '<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>',
25508                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
25509         var dn = this.dayNames;
25510         for(var i = 0; i < 7; i++){
25511             var d = this.startDay+i;
25512             if(d > 6){
25513                 d = d-7;
25514             }
25515             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
25516         }
25517         m[m.length] = "</tr></thead><tbody><tr>";
25518         for(var i = 0; i < 42; i++) {
25519             if(i % 7 == 0 && i != 0){
25520                 m[m.length] = "</tr><tr>";
25521             }
25522             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
25523         }
25524         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
25525             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
25526
25527         var el = document.createElement("div");
25528         el.className = "x-date-picker";
25529         el.innerHTML = m.join("");
25530
25531         container.dom.insertBefore(el, position);
25532
25533         this.el = Roo.get(el);
25534         this.eventEl = Roo.get(el.firstChild);
25535
25536         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
25537             handler: this.showPrevMonth,
25538             scope: this,
25539             preventDefault:true,
25540             stopDefault:true
25541         });
25542
25543         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
25544             handler: this.showNextMonth,
25545             scope: this,
25546             preventDefault:true,
25547             stopDefault:true
25548         });
25549
25550         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
25551
25552         this.monthPicker = this.el.down('div.x-date-mp');
25553         this.monthPicker.enableDisplayMode('block');
25554         
25555         var kn = new Roo.KeyNav(this.eventEl, {
25556             "left" : function(e){
25557                 e.ctrlKey ?
25558                     this.showPrevMonth() :
25559                     this.update(this.activeDate.add("d", -1));
25560             },
25561
25562             "right" : function(e){
25563                 e.ctrlKey ?
25564                     this.showNextMonth() :
25565                     this.update(this.activeDate.add("d", 1));
25566             },
25567
25568             "up" : function(e){
25569                 e.ctrlKey ?
25570                     this.showNextYear() :
25571                     this.update(this.activeDate.add("d", -7));
25572             },
25573
25574             "down" : function(e){
25575                 e.ctrlKey ?
25576                     this.showPrevYear() :
25577                     this.update(this.activeDate.add("d", 7));
25578             },
25579
25580             "pageUp" : function(e){
25581                 this.showNextMonth();
25582             },
25583
25584             "pageDown" : function(e){
25585                 this.showPrevMonth();
25586             },
25587
25588             "enter" : function(e){
25589                 e.stopPropagation();
25590                 return true;
25591             },
25592
25593             scope : this
25594         });
25595
25596         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
25597
25598         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
25599
25600         this.el.unselectable();
25601         
25602         this.cells = this.el.select("table.x-date-inner tbody td");
25603         this.textNodes = this.el.query("table.x-date-inner tbody span");
25604
25605         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
25606             text: "&#160;",
25607             tooltip: this.monthYearText
25608         });
25609
25610         this.mbtn.on('click', this.showMonthPicker, this);
25611         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
25612
25613
25614         var today = (new Date()).dateFormat(this.format);
25615         
25616         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
25617         if (this.showClear) {
25618             baseTb.add( new Roo.Toolbar.Fill());
25619         }
25620         baseTb.add({
25621             text: String.format(this.todayText, today),
25622             tooltip: String.format(this.todayTip, today),
25623             handler: this.selectToday,
25624             scope: this
25625         });
25626         
25627         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
25628             
25629         //});
25630         if (this.showClear) {
25631             
25632             baseTb.add( new Roo.Toolbar.Fill());
25633             baseTb.add({
25634                 text: '&#160;',
25635                 cls: 'x-btn-icon x-btn-clear',
25636                 handler: function() {
25637                     //this.value = '';
25638                     this.fireEvent("select", this, '');
25639                 },
25640                 scope: this
25641             });
25642         }
25643         
25644         
25645         if(Roo.isIE){
25646             this.el.repaint();
25647         }
25648         this.update(this.value);
25649     },
25650
25651     createMonthPicker : function(){
25652         if(!this.monthPicker.dom.firstChild){
25653             var buf = ['<table border="0" cellspacing="0">'];
25654             for(var i = 0; i < 6; i++){
25655                 buf.push(
25656                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
25657                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
25658                     i == 0 ?
25659                     '<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>' :
25660                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
25661                 );
25662             }
25663             buf.push(
25664                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
25665                     this.okText,
25666                     '</button><button type="button" class="x-date-mp-cancel">',
25667                     this.cancelText,
25668                     '</button></td></tr>',
25669                 '</table>'
25670             );
25671             this.monthPicker.update(buf.join(''));
25672             this.monthPicker.on('click', this.onMonthClick, this);
25673             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
25674
25675             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
25676             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
25677
25678             this.mpMonths.each(function(m, a, i){
25679                 i += 1;
25680                 if((i%2) == 0){
25681                     m.dom.xmonth = 5 + Math.round(i * .5);
25682                 }else{
25683                     m.dom.xmonth = Math.round((i-1) * .5);
25684                 }
25685             });
25686         }
25687     },
25688
25689     showMonthPicker : function(){
25690         this.createMonthPicker();
25691         var size = this.el.getSize();
25692         this.monthPicker.setSize(size);
25693         this.monthPicker.child('table').setSize(size);
25694
25695         this.mpSelMonth = (this.activeDate || this.value).getMonth();
25696         this.updateMPMonth(this.mpSelMonth);
25697         this.mpSelYear = (this.activeDate || this.value).getFullYear();
25698         this.updateMPYear(this.mpSelYear);
25699
25700         this.monthPicker.slideIn('t', {duration:.2});
25701     },
25702
25703     updateMPYear : function(y){
25704         this.mpyear = y;
25705         var ys = this.mpYears.elements;
25706         for(var i = 1; i <= 10; i++){
25707             var td = ys[i-1], y2;
25708             if((i%2) == 0){
25709                 y2 = y + Math.round(i * .5);
25710                 td.firstChild.innerHTML = y2;
25711                 td.xyear = y2;
25712             }else{
25713                 y2 = y - (5-Math.round(i * .5));
25714                 td.firstChild.innerHTML = y2;
25715                 td.xyear = y2;
25716             }
25717             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25718         }
25719     },
25720
25721     updateMPMonth : function(sm){
25722         this.mpMonths.each(function(m, a, i){
25723             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25724         });
25725     },
25726
25727     selectMPMonth: function(m){
25728         
25729     },
25730
25731     onMonthClick : function(e, t){
25732         e.stopEvent();
25733         var el = new Roo.Element(t), pn;
25734         if(el.is('button.x-date-mp-cancel')){
25735             this.hideMonthPicker();
25736         }
25737         else if(el.is('button.x-date-mp-ok')){
25738             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25739             this.hideMonthPicker();
25740         }
25741         else if(pn = el.up('td.x-date-mp-month', 2)){
25742             this.mpMonths.removeClass('x-date-mp-sel');
25743             pn.addClass('x-date-mp-sel');
25744             this.mpSelMonth = pn.dom.xmonth;
25745         }
25746         else if(pn = el.up('td.x-date-mp-year', 2)){
25747             this.mpYears.removeClass('x-date-mp-sel');
25748             pn.addClass('x-date-mp-sel');
25749             this.mpSelYear = pn.dom.xyear;
25750         }
25751         else if(el.is('a.x-date-mp-prev')){
25752             this.updateMPYear(this.mpyear-10);
25753         }
25754         else if(el.is('a.x-date-mp-next')){
25755             this.updateMPYear(this.mpyear+10);
25756         }
25757     },
25758
25759     onMonthDblClick : function(e, t){
25760         e.stopEvent();
25761         var el = new Roo.Element(t), pn;
25762         if(pn = el.up('td.x-date-mp-month', 2)){
25763             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25764             this.hideMonthPicker();
25765         }
25766         else if(pn = el.up('td.x-date-mp-year', 2)){
25767             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25768             this.hideMonthPicker();
25769         }
25770     },
25771
25772     hideMonthPicker : function(disableAnim){
25773         if(this.monthPicker){
25774             if(disableAnim === true){
25775                 this.monthPicker.hide();
25776             }else{
25777                 this.monthPicker.slideOut('t', {duration:.2});
25778             }
25779         }
25780     },
25781
25782     // private
25783     showPrevMonth : function(e){
25784         this.update(this.activeDate.add("mo", -1));
25785     },
25786
25787     // private
25788     showNextMonth : function(e){
25789         this.update(this.activeDate.add("mo", 1));
25790     },
25791
25792     // private
25793     showPrevYear : function(){
25794         this.update(this.activeDate.add("y", -1));
25795     },
25796
25797     // private
25798     showNextYear : function(){
25799         this.update(this.activeDate.add("y", 1));
25800     },
25801
25802     // private
25803     handleMouseWheel : function(e){
25804         var delta = e.getWheelDelta();
25805         if(delta > 0){
25806             this.showPrevMonth();
25807             e.stopEvent();
25808         } else if(delta < 0){
25809             this.showNextMonth();
25810             e.stopEvent();
25811         }
25812     },
25813
25814     // private
25815     handleDateClick : function(e, t){
25816         e.stopEvent();
25817         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25818             this.setValue(new Date(t.dateValue));
25819             this.fireEvent("select", this, this.value);
25820         }
25821     },
25822
25823     // private
25824     selectToday : function(){
25825         this.setValue(new Date().clearTime());
25826         this.fireEvent("select", this, this.value);
25827     },
25828
25829     // private
25830     update : function(date)
25831     {
25832         var vd = this.activeDate;
25833         this.activeDate = date;
25834         if(vd && this.el){
25835             var t = date.getTime();
25836             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25837                 this.cells.removeClass("x-date-selected");
25838                 this.cells.each(function(c){
25839                    if(c.dom.firstChild.dateValue == t){
25840                        c.addClass("x-date-selected");
25841                        setTimeout(function(){
25842                             try{c.dom.firstChild.focus();}catch(e){}
25843                        }, 50);
25844                        return false;
25845                    }
25846                 });
25847                 return;
25848             }
25849         }
25850         
25851         var days = date.getDaysInMonth();
25852         var firstOfMonth = date.getFirstDateOfMonth();
25853         var startingPos = firstOfMonth.getDay()-this.startDay;
25854
25855         if(startingPos <= this.startDay){
25856             startingPos += 7;
25857         }
25858
25859         var pm = date.add("mo", -1);
25860         var prevStart = pm.getDaysInMonth()-startingPos;
25861
25862         var cells = this.cells.elements;
25863         var textEls = this.textNodes;
25864         days += startingPos;
25865
25866         // convert everything to numbers so it's fast
25867         var day = 86400000;
25868         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25869         var today = new Date().clearTime().getTime();
25870         var sel = date.clearTime().getTime();
25871         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25872         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25873         var ddMatch = this.disabledDatesRE;
25874         var ddText = this.disabledDatesText;
25875         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25876         var ddaysText = this.disabledDaysText;
25877         var format = this.format;
25878
25879         var setCellClass = function(cal, cell){
25880             cell.title = "";
25881             var t = d.getTime();
25882             cell.firstChild.dateValue = t;
25883             if(t == today){
25884                 cell.className += " x-date-today";
25885                 cell.title = cal.todayText;
25886             }
25887             if(t == sel){
25888                 cell.className += " x-date-selected";
25889                 setTimeout(function(){
25890                     try{cell.firstChild.focus();}catch(e){}
25891                 }, 50);
25892             }
25893             // disabling
25894             if(t < min) {
25895                 cell.className = " x-date-disabled";
25896                 cell.title = cal.minText;
25897                 return;
25898             }
25899             if(t > max) {
25900                 cell.className = " x-date-disabled";
25901                 cell.title = cal.maxText;
25902                 return;
25903             }
25904             if(ddays){
25905                 if(ddays.indexOf(d.getDay()) != -1){
25906                     cell.title = ddaysText;
25907                     cell.className = " x-date-disabled";
25908                 }
25909             }
25910             if(ddMatch && format){
25911                 var fvalue = d.dateFormat(format);
25912                 if(ddMatch.test(fvalue)){
25913                     cell.title = ddText.replace("%0", fvalue);
25914                     cell.className = " x-date-disabled";
25915                 }
25916             }
25917         };
25918
25919         var i = 0;
25920         for(; i < startingPos; i++) {
25921             textEls[i].innerHTML = (++prevStart);
25922             d.setDate(d.getDate()+1);
25923             cells[i].className = "x-date-prevday";
25924             setCellClass(this, cells[i]);
25925         }
25926         for(; i < days; i++){
25927             intDay = i - startingPos + 1;
25928             textEls[i].innerHTML = (intDay);
25929             d.setDate(d.getDate()+1);
25930             cells[i].className = "x-date-active";
25931             setCellClass(this, cells[i]);
25932         }
25933         var extraDays = 0;
25934         for(; i < 42; i++) {
25935              textEls[i].innerHTML = (++extraDays);
25936              d.setDate(d.getDate()+1);
25937              cells[i].className = "x-date-nextday";
25938              setCellClass(this, cells[i]);
25939         }
25940
25941         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25942         this.fireEvent('monthchange', this, date);
25943         
25944         if(!this.internalRender){
25945             var main = this.el.dom.firstChild;
25946             var w = main.offsetWidth;
25947             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25948             Roo.fly(main).setWidth(w);
25949             this.internalRender = true;
25950             // opera does not respect the auto grow header center column
25951             // then, after it gets a width opera refuses to recalculate
25952             // without a second pass
25953             if(Roo.isOpera && !this.secondPass){
25954                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25955                 this.secondPass = true;
25956                 this.update.defer(10, this, [date]);
25957             }
25958         }
25959         
25960         
25961     }
25962 });        /*
25963  * Based on:
25964  * Ext JS Library 1.1.1
25965  * Copyright(c) 2006-2007, Ext JS, LLC.
25966  *
25967  * Originally Released Under LGPL - original licence link has changed is not relivant.
25968  *
25969  * Fork - LGPL
25970  * <script type="text/javascript">
25971  */
25972 /**
25973  * @class Roo.TabPanel
25974  * @extends Roo.util.Observable
25975  * A lightweight tab container.
25976  * <br><br>
25977  * Usage:
25978  * <pre><code>
25979 // basic tabs 1, built from existing content
25980 var tabs = new Roo.TabPanel("tabs1");
25981 tabs.addTab("script", "View Script");
25982 tabs.addTab("markup", "View Markup");
25983 tabs.activate("script");
25984
25985 // more advanced tabs, built from javascript
25986 var jtabs = new Roo.TabPanel("jtabs");
25987 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25988
25989 // set up the UpdateManager
25990 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25991 var updater = tab2.getUpdateManager();
25992 updater.setDefaultUrl("ajax1.htm");
25993 tab2.on('activate', updater.refresh, updater, true);
25994
25995 // Use setUrl for Ajax loading
25996 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25997 tab3.setUrl("ajax2.htm", null, true);
25998
25999 // Disabled tab
26000 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26001 tab4.disable();
26002
26003 jtabs.activate("jtabs-1");
26004  * </code></pre>
26005  * @constructor
26006  * Create a new TabPanel.
26007  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26008  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26009  */
26010 Roo.TabPanel = function(container, config){
26011     /**
26012     * The container element for this TabPanel.
26013     * @type Roo.Element
26014     */
26015     this.el = Roo.get(container, true);
26016     if(config){
26017         if(typeof config == "boolean"){
26018             this.tabPosition = config ? "bottom" : "top";
26019         }else{
26020             Roo.apply(this, config);
26021         }
26022     }
26023     if(this.tabPosition == "bottom"){
26024         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26025         this.el.addClass("x-tabs-bottom");
26026     }
26027     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26028     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26029     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26030     if(Roo.isIE){
26031         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26032     }
26033     if(this.tabPosition != "bottom"){
26034         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26035          * @type Roo.Element
26036          */
26037         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26038         this.el.addClass("x-tabs-top");
26039     }
26040     this.items = [];
26041
26042     this.bodyEl.setStyle("position", "relative");
26043
26044     this.active = null;
26045     this.activateDelegate = this.activate.createDelegate(this);
26046
26047     this.addEvents({
26048         /**
26049          * @event tabchange
26050          * Fires when the active tab changes
26051          * @param {Roo.TabPanel} this
26052          * @param {Roo.TabPanelItem} activePanel The new active tab
26053          */
26054         "tabchange": true,
26055         /**
26056          * @event beforetabchange
26057          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26058          * @param {Roo.TabPanel} this
26059          * @param {Object} e Set cancel to true on this object to cancel the tab change
26060          * @param {Roo.TabPanelItem} tab The tab being changed to
26061          */
26062         "beforetabchange" : true
26063     });
26064
26065     Roo.EventManager.onWindowResize(this.onResize, this);
26066     this.cpad = this.el.getPadding("lr");
26067     this.hiddenCount = 0;
26068
26069
26070     // toolbar on the tabbar support...
26071     if (this.toolbar) {
26072         var tcfg = this.toolbar;
26073         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26074         this.toolbar = new Roo.Toolbar(tcfg);
26075         if (Roo.isSafari) {
26076             var tbl = tcfg.container.child('table', true);
26077             tbl.setAttribute('width', '100%');
26078         }
26079         
26080     }
26081    
26082
26083
26084     Roo.TabPanel.superclass.constructor.call(this);
26085 };
26086
26087 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26088     /*
26089      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26090      */
26091     tabPosition : "top",
26092     /*
26093      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26094      */
26095     currentTabWidth : 0,
26096     /*
26097      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26098      */
26099     minTabWidth : 40,
26100     /*
26101      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26102      */
26103     maxTabWidth : 250,
26104     /*
26105      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26106      */
26107     preferredTabWidth : 175,
26108     /*
26109      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26110      */
26111     resizeTabs : false,
26112     /*
26113      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26114      */
26115     monitorResize : true,
26116     /*
26117      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26118      */
26119     toolbar : false,
26120
26121     /**
26122      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26123      * @param {String} id The id of the div to use <b>or create</b>
26124      * @param {String} text The text for the tab
26125      * @param {String} content (optional) Content to put in the TabPanelItem body
26126      * @param {Boolean} closable (optional) True to create a close icon on the tab
26127      * @return {Roo.TabPanelItem} The created TabPanelItem
26128      */
26129     addTab : function(id, text, content, closable){
26130         var item = new Roo.TabPanelItem(this, id, text, closable);
26131         this.addTabItem(item);
26132         if(content){
26133             item.setContent(content);
26134         }
26135         return item;
26136     },
26137
26138     /**
26139      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26140      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26141      * @return {Roo.TabPanelItem}
26142      */
26143     getTab : function(id){
26144         return this.items[id];
26145     },
26146
26147     /**
26148      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26149      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26150      */
26151     hideTab : function(id){
26152         var t = this.items[id];
26153         if(!t.isHidden()){
26154            t.setHidden(true);
26155            this.hiddenCount++;
26156            this.autoSizeTabs();
26157         }
26158     },
26159
26160     /**
26161      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26162      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26163      */
26164     unhideTab : function(id){
26165         var t = this.items[id];
26166         if(t.isHidden()){
26167            t.setHidden(false);
26168            this.hiddenCount--;
26169            this.autoSizeTabs();
26170         }
26171     },
26172
26173     /**
26174      * Adds an existing {@link Roo.TabPanelItem}.
26175      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26176      */
26177     addTabItem : function(item){
26178         this.items[item.id] = item;
26179         this.items.push(item);
26180         if(this.resizeTabs){
26181            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26182            this.autoSizeTabs();
26183         }else{
26184             item.autoSize();
26185         }
26186     },
26187
26188     /**
26189      * Removes a {@link Roo.TabPanelItem}.
26190      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26191      */
26192     removeTab : function(id){
26193         var items = this.items;
26194         var tab = items[id];
26195         if(!tab) { return; }
26196         var index = items.indexOf(tab);
26197         if(this.active == tab && items.length > 1){
26198             var newTab = this.getNextAvailable(index);
26199             if(newTab) {
26200                 newTab.activate();
26201             }
26202         }
26203         this.stripEl.dom.removeChild(tab.pnode.dom);
26204         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26205             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26206         }
26207         items.splice(index, 1);
26208         delete this.items[tab.id];
26209         tab.fireEvent("close", tab);
26210         tab.purgeListeners();
26211         this.autoSizeTabs();
26212     },
26213
26214     getNextAvailable : function(start){
26215         var items = this.items;
26216         var index = start;
26217         // look for a next tab that will slide over to
26218         // replace the one being removed
26219         while(index < items.length){
26220             var item = items[++index];
26221             if(item && !item.isHidden()){
26222                 return item;
26223             }
26224         }
26225         // if one isn't found select the previous tab (on the left)
26226         index = start;
26227         while(index >= 0){
26228             var item = items[--index];
26229             if(item && !item.isHidden()){
26230                 return item;
26231             }
26232         }
26233         return null;
26234     },
26235
26236     /**
26237      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26238      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26239      */
26240     disableTab : function(id){
26241         var tab = this.items[id];
26242         if(tab && this.active != tab){
26243             tab.disable();
26244         }
26245     },
26246
26247     /**
26248      * Enables a {@link Roo.TabPanelItem} that is disabled.
26249      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26250      */
26251     enableTab : function(id){
26252         var tab = this.items[id];
26253         tab.enable();
26254     },
26255
26256     /**
26257      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26258      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26259      * @return {Roo.TabPanelItem} The TabPanelItem.
26260      */
26261     activate : function(id){
26262         var tab = this.items[id];
26263         if(!tab){
26264             return null;
26265         }
26266         if(tab == this.active || tab.disabled){
26267             return tab;
26268         }
26269         var e = {};
26270         this.fireEvent("beforetabchange", this, e, tab);
26271         if(e.cancel !== true && !tab.disabled){
26272             if(this.active){
26273                 this.active.hide();
26274             }
26275             this.active = this.items[id];
26276             this.active.show();
26277             this.fireEvent("tabchange", this, this.active);
26278         }
26279         return tab;
26280     },
26281
26282     /**
26283      * Gets the active {@link Roo.TabPanelItem}.
26284      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26285      */
26286     getActiveTab : function(){
26287         return this.active;
26288     },
26289
26290     /**
26291      * Updates the tab body element to fit the height of the container element
26292      * for overflow scrolling
26293      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26294      */
26295     syncHeight : function(targetHeight){
26296         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26297         var bm = this.bodyEl.getMargins();
26298         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26299         this.bodyEl.setHeight(newHeight);
26300         return newHeight;
26301     },
26302
26303     onResize : function(){
26304         if(this.monitorResize){
26305             this.autoSizeTabs();
26306         }
26307     },
26308
26309     /**
26310      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26311      */
26312     beginUpdate : function(){
26313         this.updating = true;
26314     },
26315
26316     /**
26317      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26318      */
26319     endUpdate : function(){
26320         this.updating = false;
26321         this.autoSizeTabs();
26322     },
26323
26324     /**
26325      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26326      */
26327     autoSizeTabs : function(){
26328         var count = this.items.length;
26329         var vcount = count - this.hiddenCount;
26330         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26331         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26332         var availWidth = Math.floor(w / vcount);
26333         var b = this.stripBody;
26334         if(b.getWidth() > w){
26335             var tabs = this.items;
26336             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26337             if(availWidth < this.minTabWidth){
26338                 /*if(!this.sleft){    // incomplete scrolling code
26339                     this.createScrollButtons();
26340                 }
26341                 this.showScroll();
26342                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26343             }
26344         }else{
26345             if(this.currentTabWidth < this.preferredTabWidth){
26346                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26347             }
26348         }
26349     },
26350
26351     /**
26352      * Returns the number of tabs in this TabPanel.
26353      * @return {Number}
26354      */
26355      getCount : function(){
26356          return this.items.length;
26357      },
26358
26359     /**
26360      * Resizes all the tabs to the passed width
26361      * @param {Number} The new width
26362      */
26363     setTabWidth : function(width){
26364         this.currentTabWidth = width;
26365         for(var i = 0, len = this.items.length; i < len; i++) {
26366                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26367         }
26368     },
26369
26370     /**
26371      * Destroys this TabPanel
26372      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26373      */
26374     destroy : function(removeEl){
26375         Roo.EventManager.removeResizeListener(this.onResize, this);
26376         for(var i = 0, len = this.items.length; i < len; i++){
26377             this.items[i].purgeListeners();
26378         }
26379         if(removeEl === true){
26380             this.el.update("");
26381             this.el.remove();
26382         }
26383     }
26384 });
26385
26386 /**
26387  * @class Roo.TabPanelItem
26388  * @extends Roo.util.Observable
26389  * Represents an individual item (tab plus body) in a TabPanel.
26390  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26391  * @param {String} id The id of this TabPanelItem
26392  * @param {String} text The text for the tab of this TabPanelItem
26393  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26394  */
26395 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26396     /**
26397      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26398      * @type Roo.TabPanel
26399      */
26400     this.tabPanel = tabPanel;
26401     /**
26402      * The id for this TabPanelItem
26403      * @type String
26404      */
26405     this.id = id;
26406     /** @private */
26407     this.disabled = false;
26408     /** @private */
26409     this.text = text;
26410     /** @private */
26411     this.loaded = false;
26412     this.closable = closable;
26413
26414     /**
26415      * The body element for this TabPanelItem.
26416      * @type Roo.Element
26417      */
26418     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26419     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26420     this.bodyEl.setStyle("display", "block");
26421     this.bodyEl.setStyle("zoom", "1");
26422     this.hideAction();
26423
26424     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26425     /** @private */
26426     this.el = Roo.get(els.el, true);
26427     this.inner = Roo.get(els.inner, true);
26428     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26429     this.pnode = Roo.get(els.el.parentNode, true);
26430     this.el.on("mousedown", this.onTabMouseDown, this);
26431     this.el.on("click", this.onTabClick, this);
26432     /** @private */
26433     if(closable){
26434         var c = Roo.get(els.close, true);
26435         c.dom.title = this.closeText;
26436         c.addClassOnOver("close-over");
26437         c.on("click", this.closeClick, this);
26438      }
26439
26440     this.addEvents({
26441          /**
26442          * @event activate
26443          * Fires when this tab becomes the active tab.
26444          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26445          * @param {Roo.TabPanelItem} this
26446          */
26447         "activate": true,
26448         /**
26449          * @event beforeclose
26450          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26451          * @param {Roo.TabPanelItem} this
26452          * @param {Object} e Set cancel to true on this object to cancel the close.
26453          */
26454         "beforeclose": true,
26455         /**
26456          * @event close
26457          * Fires when this tab is closed.
26458          * @param {Roo.TabPanelItem} this
26459          */
26460          "close": true,
26461         /**
26462          * @event deactivate
26463          * Fires when this tab is no longer the active tab.
26464          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26465          * @param {Roo.TabPanelItem} this
26466          */
26467          "deactivate" : true
26468     });
26469     this.hidden = false;
26470
26471     Roo.TabPanelItem.superclass.constructor.call(this);
26472 };
26473
26474 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26475     purgeListeners : function(){
26476        Roo.util.Observable.prototype.purgeListeners.call(this);
26477        this.el.removeAllListeners();
26478     },
26479     /**
26480      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26481      */
26482     show : function(){
26483         this.pnode.addClass("on");
26484         this.showAction();
26485         if(Roo.isOpera){
26486             this.tabPanel.stripWrap.repaint();
26487         }
26488         this.fireEvent("activate", this.tabPanel, this);
26489     },
26490
26491     /**
26492      * Returns true if this tab is the active tab.
26493      * @return {Boolean}
26494      */
26495     isActive : function(){
26496         return this.tabPanel.getActiveTab() == this;
26497     },
26498
26499     /**
26500      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
26501      */
26502     hide : function(){
26503         this.pnode.removeClass("on");
26504         this.hideAction();
26505         this.fireEvent("deactivate", this.tabPanel, this);
26506     },
26507
26508     hideAction : function(){
26509         this.bodyEl.hide();
26510         this.bodyEl.setStyle("position", "absolute");
26511         this.bodyEl.setLeft("-20000px");
26512         this.bodyEl.setTop("-20000px");
26513     },
26514
26515     showAction : function(){
26516         this.bodyEl.setStyle("position", "relative");
26517         this.bodyEl.setTop("");
26518         this.bodyEl.setLeft("");
26519         this.bodyEl.show();
26520     },
26521
26522     /**
26523      * Set the tooltip for the tab.
26524      * @param {String} tooltip The tab's tooltip
26525      */
26526     setTooltip : function(text){
26527         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
26528             this.textEl.dom.qtip = text;
26529             this.textEl.dom.removeAttribute('title');
26530         }else{
26531             this.textEl.dom.title = text;
26532         }
26533     },
26534
26535     onTabClick : function(e){
26536         e.preventDefault();
26537         this.tabPanel.activate(this.id);
26538     },
26539
26540     onTabMouseDown : function(e){
26541         e.preventDefault();
26542         this.tabPanel.activate(this.id);
26543     },
26544
26545     getWidth : function(){
26546         return this.inner.getWidth();
26547     },
26548
26549     setWidth : function(width){
26550         var iwidth = width - this.pnode.getPadding("lr");
26551         this.inner.setWidth(iwidth);
26552         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
26553         this.pnode.setWidth(width);
26554     },
26555
26556     /**
26557      * Show or hide the tab
26558      * @param {Boolean} hidden True to hide or false to show.
26559      */
26560     setHidden : function(hidden){
26561         this.hidden = hidden;
26562         this.pnode.setStyle("display", hidden ? "none" : "");
26563     },
26564
26565     /**
26566      * Returns true if this tab is "hidden"
26567      * @return {Boolean}
26568      */
26569     isHidden : function(){
26570         return this.hidden;
26571     },
26572
26573     /**
26574      * Returns the text for this tab
26575      * @return {String}
26576      */
26577     getText : function(){
26578         return this.text;
26579     },
26580
26581     autoSize : function(){
26582         //this.el.beginMeasure();
26583         this.textEl.setWidth(1);
26584         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
26585         //this.el.endMeasure();
26586     },
26587
26588     /**
26589      * Sets the text for the tab (Note: this also sets the tooltip text)
26590      * @param {String} text The tab's text and tooltip
26591      */
26592     setText : function(text){
26593         this.text = text;
26594         this.textEl.update(text);
26595         this.setTooltip(text);
26596         if(!this.tabPanel.resizeTabs){
26597             this.autoSize();
26598         }
26599     },
26600     /**
26601      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
26602      */
26603     activate : function(){
26604         this.tabPanel.activate(this.id);
26605     },
26606
26607     /**
26608      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
26609      */
26610     disable : function(){
26611         if(this.tabPanel.active != this){
26612             this.disabled = true;
26613             this.pnode.addClass("disabled");
26614         }
26615     },
26616
26617     /**
26618      * Enables this TabPanelItem if it was previously disabled.
26619      */
26620     enable : function(){
26621         this.disabled = false;
26622         this.pnode.removeClass("disabled");
26623     },
26624
26625     /**
26626      * Sets the content for this TabPanelItem.
26627      * @param {String} content The content
26628      * @param {Boolean} loadScripts true to look for and load scripts
26629      */
26630     setContent : function(content, loadScripts){
26631         this.bodyEl.update(content, loadScripts);
26632     },
26633
26634     /**
26635      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
26636      * @return {Roo.UpdateManager} The UpdateManager
26637      */
26638     getUpdateManager : function(){
26639         return this.bodyEl.getUpdateManager();
26640     },
26641
26642     /**
26643      * Set a URL to be used to load the content for this TabPanelItem.
26644      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
26645      * @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)
26646      * @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)
26647      * @return {Roo.UpdateManager} The UpdateManager
26648      */
26649     setUrl : function(url, params, loadOnce){
26650         if(this.refreshDelegate){
26651             this.un('activate', this.refreshDelegate);
26652         }
26653         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
26654         this.on("activate", this.refreshDelegate);
26655         return this.bodyEl.getUpdateManager();
26656     },
26657
26658     /** @private */
26659     _handleRefresh : function(url, params, loadOnce){
26660         if(!loadOnce || !this.loaded){
26661             var updater = this.bodyEl.getUpdateManager();
26662             updater.update(url, params, this._setLoaded.createDelegate(this));
26663         }
26664     },
26665
26666     /**
26667      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
26668      *   Will fail silently if the setUrl method has not been called.
26669      *   This does not activate the panel, just updates its content.
26670      */
26671     refresh : function(){
26672         if(this.refreshDelegate){
26673            this.loaded = false;
26674            this.refreshDelegate();
26675         }
26676     },
26677
26678     /** @private */
26679     _setLoaded : function(){
26680         this.loaded = true;
26681     },
26682
26683     /** @private */
26684     closeClick : function(e){
26685         var o = {};
26686         e.stopEvent();
26687         this.fireEvent("beforeclose", this, o);
26688         if(o.cancel !== true){
26689             this.tabPanel.removeTab(this.id);
26690         }
26691     },
26692     /**
26693      * The text displayed in the tooltip for the close icon.
26694      * @type String
26695      */
26696     closeText : "Close this tab"
26697 });
26698
26699 /** @private */
26700 Roo.TabPanel.prototype.createStrip = function(container){
26701     var strip = document.createElement("div");
26702     strip.className = "x-tabs-wrap";
26703     container.appendChild(strip);
26704     return strip;
26705 };
26706 /** @private */
26707 Roo.TabPanel.prototype.createStripList = function(strip){
26708     // div wrapper for retard IE
26709     // returns the "tr" element.
26710     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26711         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26712         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26713     return strip.firstChild.firstChild.firstChild.firstChild;
26714 };
26715 /** @private */
26716 Roo.TabPanel.prototype.createBody = function(container){
26717     var body = document.createElement("div");
26718     Roo.id(body, "tab-body");
26719     Roo.fly(body).addClass("x-tabs-body");
26720     container.appendChild(body);
26721     return body;
26722 };
26723 /** @private */
26724 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26725     var body = Roo.getDom(id);
26726     if(!body){
26727         body = document.createElement("div");
26728         body.id = id;
26729     }
26730     Roo.fly(body).addClass("x-tabs-item-body");
26731     bodyEl.insertBefore(body, bodyEl.firstChild);
26732     return body;
26733 };
26734 /** @private */
26735 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26736     var td = document.createElement("td");
26737     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26738     //stripEl.appendChild(td);
26739     if(closable){
26740         td.className = "x-tabs-closable";
26741         if(!this.closeTpl){
26742             this.closeTpl = new Roo.Template(
26743                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26744                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26745                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26746             );
26747         }
26748         var el = this.closeTpl.overwrite(td, {"text": text});
26749         var close = el.getElementsByTagName("div")[0];
26750         var inner = el.getElementsByTagName("em")[0];
26751         return {"el": el, "close": close, "inner": inner};
26752     } else {
26753         if(!this.tabTpl){
26754             this.tabTpl = new Roo.Template(
26755                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26756                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26757             );
26758         }
26759         var el = this.tabTpl.overwrite(td, {"text": text});
26760         var inner = el.getElementsByTagName("em")[0];
26761         return {"el": el, "inner": inner};
26762     }
26763 };/*
26764  * Based on:
26765  * Ext JS Library 1.1.1
26766  * Copyright(c) 2006-2007, Ext JS, LLC.
26767  *
26768  * Originally Released Under LGPL - original licence link has changed is not relivant.
26769  *
26770  * Fork - LGPL
26771  * <script type="text/javascript">
26772  */
26773
26774 /**
26775  * @class Roo.Button
26776  * @extends Roo.util.Observable
26777  * Simple Button class
26778  * @cfg {String} text The button text
26779  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26780  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26781  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26782  * @cfg {Object} scope The scope of the handler
26783  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26784  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26785  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26786  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26787  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26788  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26789    applies if enableToggle = true)
26790  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26791  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26792   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26793  * @constructor
26794  * Create a new button
26795  * @param {Object} config The config object
26796  */
26797 Roo.Button = function(renderTo, config)
26798 {
26799     if (!config) {
26800         config = renderTo;
26801         renderTo = config.renderTo || false;
26802     }
26803     
26804     Roo.apply(this, config);
26805     this.addEvents({
26806         /**
26807              * @event click
26808              * Fires when this button is clicked
26809              * @param {Button} this
26810              * @param {EventObject} e The click event
26811              */
26812             "click" : true,
26813         /**
26814              * @event toggle
26815              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26816              * @param {Button} this
26817              * @param {Boolean} pressed
26818              */
26819             "toggle" : true,
26820         /**
26821              * @event mouseover
26822              * Fires when the mouse hovers over the button
26823              * @param {Button} this
26824              * @param {Event} e The event object
26825              */
26826         'mouseover' : true,
26827         /**
26828              * @event mouseout
26829              * Fires when the mouse exits the button
26830              * @param {Button} this
26831              * @param {Event} e The event object
26832              */
26833         'mouseout': true,
26834          /**
26835              * @event render
26836              * Fires when the button is rendered
26837              * @param {Button} this
26838              */
26839         'render': true
26840     });
26841     if(this.menu){
26842         this.menu = Roo.menu.MenuMgr.get(this.menu);
26843     }
26844     // register listeners first!!  - so render can be captured..
26845     Roo.util.Observable.call(this);
26846     if(renderTo){
26847         this.render(renderTo);
26848     }
26849     
26850   
26851 };
26852
26853 Roo.extend(Roo.Button, Roo.util.Observable, {
26854     /**
26855      * 
26856      */
26857     
26858     /**
26859      * Read-only. True if this button is hidden
26860      * @type Boolean
26861      */
26862     hidden : false,
26863     /**
26864      * Read-only. True if this button is disabled
26865      * @type Boolean
26866      */
26867     disabled : false,
26868     /**
26869      * Read-only. True if this button is pressed (only if enableToggle = true)
26870      * @type Boolean
26871      */
26872     pressed : false,
26873
26874     /**
26875      * @cfg {Number} tabIndex 
26876      * The DOM tabIndex for this button (defaults to undefined)
26877      */
26878     tabIndex : undefined,
26879
26880     /**
26881      * @cfg {Boolean} enableToggle
26882      * True to enable pressed/not pressed toggling (defaults to false)
26883      */
26884     enableToggle: false,
26885     /**
26886      * @cfg {Mixed} menu
26887      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26888      */
26889     menu : undefined,
26890     /**
26891      * @cfg {String} menuAlign
26892      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26893      */
26894     menuAlign : "tl-bl?",
26895
26896     /**
26897      * @cfg {String} iconCls
26898      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26899      */
26900     iconCls : undefined,
26901     /**
26902      * @cfg {String} type
26903      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26904      */
26905     type : 'button',
26906
26907     // private
26908     menuClassTarget: 'tr',
26909
26910     /**
26911      * @cfg {String} clickEvent
26912      * The type of event to map to the button's event handler (defaults to 'click')
26913      */
26914     clickEvent : 'click',
26915
26916     /**
26917      * @cfg {Boolean} handleMouseEvents
26918      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26919      */
26920     handleMouseEvents : true,
26921
26922     /**
26923      * @cfg {String} tooltipType
26924      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26925      */
26926     tooltipType : 'qtip',
26927
26928     /**
26929      * @cfg {String} cls
26930      * A CSS class to apply to the button's main element.
26931      */
26932     
26933     /**
26934      * @cfg {Roo.Template} template (Optional)
26935      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26936      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26937      * require code modifications if required elements (e.g. a button) aren't present.
26938      */
26939
26940     // private
26941     render : function(renderTo){
26942         var btn;
26943         if(this.hideParent){
26944             this.parentEl = Roo.get(renderTo);
26945         }
26946         if(!this.dhconfig){
26947             if(!this.template){
26948                 if(!Roo.Button.buttonTemplate){
26949                     // hideous table template
26950                     Roo.Button.buttonTemplate = new Roo.Template(
26951                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26952                         '<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>',
26953                         "</tr></tbody></table>");
26954                 }
26955                 this.template = Roo.Button.buttonTemplate;
26956             }
26957             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26958             var btnEl = btn.child("button:first");
26959             btnEl.on('focus', this.onFocus, this);
26960             btnEl.on('blur', this.onBlur, this);
26961             if(this.cls){
26962                 btn.addClass(this.cls);
26963             }
26964             if(this.icon){
26965                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26966             }
26967             if(this.iconCls){
26968                 btnEl.addClass(this.iconCls);
26969                 if(!this.cls){
26970                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26971                 }
26972             }
26973             if(this.tabIndex !== undefined){
26974                 btnEl.dom.tabIndex = this.tabIndex;
26975             }
26976             if(this.tooltip){
26977                 if(typeof this.tooltip == 'object'){
26978                     Roo.QuickTips.tips(Roo.apply({
26979                           target: btnEl.id
26980                     }, this.tooltip));
26981                 } else {
26982                     btnEl.dom[this.tooltipType] = this.tooltip;
26983                 }
26984             }
26985         }else{
26986             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26987         }
26988         this.el = btn;
26989         if(this.id){
26990             this.el.dom.id = this.el.id = this.id;
26991         }
26992         if(this.menu){
26993             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26994             this.menu.on("show", this.onMenuShow, this);
26995             this.menu.on("hide", this.onMenuHide, this);
26996         }
26997         btn.addClass("x-btn");
26998         if(Roo.isIE && !Roo.isIE7){
26999             this.autoWidth.defer(1, this);
27000         }else{
27001             this.autoWidth();
27002         }
27003         if(this.handleMouseEvents){
27004             btn.on("mouseover", this.onMouseOver, this);
27005             btn.on("mouseout", this.onMouseOut, this);
27006             btn.on("mousedown", this.onMouseDown, this);
27007         }
27008         btn.on(this.clickEvent, this.onClick, this);
27009         //btn.on("mouseup", this.onMouseUp, this);
27010         if(this.hidden){
27011             this.hide();
27012         }
27013         if(this.disabled){
27014             this.disable();
27015         }
27016         Roo.ButtonToggleMgr.register(this);
27017         if(this.pressed){
27018             this.el.addClass("x-btn-pressed");
27019         }
27020         if(this.repeat){
27021             var repeater = new Roo.util.ClickRepeater(btn,
27022                 typeof this.repeat == "object" ? this.repeat : {}
27023             );
27024             repeater.on("click", this.onClick,  this);
27025         }
27026         
27027         this.fireEvent('render', this);
27028         
27029     },
27030     /**
27031      * Returns the button's underlying element
27032      * @return {Roo.Element} The element
27033      */
27034     getEl : function(){
27035         return this.el;  
27036     },
27037     
27038     /**
27039      * Destroys this Button and removes any listeners.
27040      */
27041     destroy : function(){
27042         Roo.ButtonToggleMgr.unregister(this);
27043         this.el.removeAllListeners();
27044         this.purgeListeners();
27045         this.el.remove();
27046     },
27047
27048     // private
27049     autoWidth : function(){
27050         if(this.el){
27051             this.el.setWidth("auto");
27052             if(Roo.isIE7 && Roo.isStrict){
27053                 var ib = this.el.child('button');
27054                 if(ib && ib.getWidth() > 20){
27055                     ib.clip();
27056                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27057                 }
27058             }
27059             if(this.minWidth){
27060                 if(this.hidden){
27061                     this.el.beginMeasure();
27062                 }
27063                 if(this.el.getWidth() < this.minWidth){
27064                     this.el.setWidth(this.minWidth);
27065                 }
27066                 if(this.hidden){
27067                     this.el.endMeasure();
27068                 }
27069             }
27070         }
27071     },
27072
27073     /**
27074      * Assigns this button's click handler
27075      * @param {Function} handler The function to call when the button is clicked
27076      * @param {Object} scope (optional) Scope for the function passed in
27077      */
27078     setHandler : function(handler, scope){
27079         this.handler = handler;
27080         this.scope = scope;  
27081     },
27082     
27083     /**
27084      * Sets this button's text
27085      * @param {String} text The button text
27086      */
27087     setText : function(text){
27088         this.text = text;
27089         if(this.el){
27090             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27091         }
27092         this.autoWidth();
27093     },
27094     
27095     /**
27096      * Gets the text for this button
27097      * @return {String} The button text
27098      */
27099     getText : function(){
27100         return this.text;  
27101     },
27102     
27103     /**
27104      * Show this button
27105      */
27106     show: function(){
27107         this.hidden = false;
27108         if(this.el){
27109             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27110         }
27111     },
27112     
27113     /**
27114      * Hide this button
27115      */
27116     hide: function(){
27117         this.hidden = true;
27118         if(this.el){
27119             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27120         }
27121     },
27122     
27123     /**
27124      * Convenience function for boolean show/hide
27125      * @param {Boolean} visible True to show, false to hide
27126      */
27127     setVisible: function(visible){
27128         if(visible) {
27129             this.show();
27130         }else{
27131             this.hide();
27132         }
27133     },
27134     
27135     /**
27136      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27137      * @param {Boolean} state (optional) Force a particular state
27138      */
27139     toggle : function(state){
27140         state = state === undefined ? !this.pressed : state;
27141         if(state != this.pressed){
27142             if(state){
27143                 this.el.addClass("x-btn-pressed");
27144                 this.pressed = true;
27145                 this.fireEvent("toggle", this, true);
27146             }else{
27147                 this.el.removeClass("x-btn-pressed");
27148                 this.pressed = false;
27149                 this.fireEvent("toggle", this, false);
27150             }
27151             if(this.toggleHandler){
27152                 this.toggleHandler.call(this.scope || this, this, state);
27153             }
27154         }
27155     },
27156     
27157     /**
27158      * Focus the button
27159      */
27160     focus : function(){
27161         this.el.child('button:first').focus();
27162     },
27163     
27164     /**
27165      * Disable this button
27166      */
27167     disable : function(){
27168         if(this.el){
27169             this.el.addClass("x-btn-disabled");
27170         }
27171         this.disabled = true;
27172     },
27173     
27174     /**
27175      * Enable this button
27176      */
27177     enable : function(){
27178         if(this.el){
27179             this.el.removeClass("x-btn-disabled");
27180         }
27181         this.disabled = false;
27182     },
27183
27184     /**
27185      * Convenience function for boolean enable/disable
27186      * @param {Boolean} enabled True to enable, false to disable
27187      */
27188     setDisabled : function(v){
27189         this[v !== true ? "enable" : "disable"]();
27190     },
27191
27192     // private
27193     onClick : function(e){
27194         if(e){
27195             e.preventDefault();
27196         }
27197         if(e.button != 0){
27198             return;
27199         }
27200         if(!this.disabled){
27201             if(this.enableToggle){
27202                 this.toggle();
27203             }
27204             if(this.menu && !this.menu.isVisible()){
27205                 this.menu.show(this.el, this.menuAlign);
27206             }
27207             this.fireEvent("click", this, e);
27208             if(this.handler){
27209                 this.el.removeClass("x-btn-over");
27210                 this.handler.call(this.scope || this, this, e);
27211             }
27212         }
27213     },
27214     // private
27215     onMouseOver : function(e){
27216         if(!this.disabled){
27217             this.el.addClass("x-btn-over");
27218             this.fireEvent('mouseover', this, e);
27219         }
27220     },
27221     // private
27222     onMouseOut : function(e){
27223         if(!e.within(this.el,  true)){
27224             this.el.removeClass("x-btn-over");
27225             this.fireEvent('mouseout', this, e);
27226         }
27227     },
27228     // private
27229     onFocus : function(e){
27230         if(!this.disabled){
27231             this.el.addClass("x-btn-focus");
27232         }
27233     },
27234     // private
27235     onBlur : function(e){
27236         this.el.removeClass("x-btn-focus");
27237     },
27238     // private
27239     onMouseDown : function(e){
27240         if(!this.disabled && e.button == 0){
27241             this.el.addClass("x-btn-click");
27242             Roo.get(document).on('mouseup', this.onMouseUp, this);
27243         }
27244     },
27245     // private
27246     onMouseUp : function(e){
27247         if(e.button == 0){
27248             this.el.removeClass("x-btn-click");
27249             Roo.get(document).un('mouseup', this.onMouseUp, this);
27250         }
27251     },
27252     // private
27253     onMenuShow : function(e){
27254         this.el.addClass("x-btn-menu-active");
27255     },
27256     // private
27257     onMenuHide : function(e){
27258         this.el.removeClass("x-btn-menu-active");
27259     }   
27260 });
27261
27262 // Private utility class used by Button
27263 Roo.ButtonToggleMgr = function(){
27264    var groups = {};
27265    
27266    function toggleGroup(btn, state){
27267        if(state){
27268            var g = groups[btn.toggleGroup];
27269            for(var i = 0, l = g.length; i < l; i++){
27270                if(g[i] != btn){
27271                    g[i].toggle(false);
27272                }
27273            }
27274        }
27275    }
27276    
27277    return {
27278        register : function(btn){
27279            if(!btn.toggleGroup){
27280                return;
27281            }
27282            var g = groups[btn.toggleGroup];
27283            if(!g){
27284                g = groups[btn.toggleGroup] = [];
27285            }
27286            g.push(btn);
27287            btn.on("toggle", toggleGroup);
27288        },
27289        
27290        unregister : function(btn){
27291            if(!btn.toggleGroup){
27292                return;
27293            }
27294            var g = groups[btn.toggleGroup];
27295            if(g){
27296                g.remove(btn);
27297                btn.un("toggle", toggleGroup);
27298            }
27299        }
27300    };
27301 }();/*
27302  * Based on:
27303  * Ext JS Library 1.1.1
27304  * Copyright(c) 2006-2007, Ext JS, LLC.
27305  *
27306  * Originally Released Under LGPL - original licence link has changed is not relivant.
27307  *
27308  * Fork - LGPL
27309  * <script type="text/javascript">
27310  */
27311  
27312 /**
27313  * @class Roo.SplitButton
27314  * @extends Roo.Button
27315  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27316  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27317  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27318  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27319  * @cfg {String} arrowTooltip The title attribute of the arrow
27320  * @constructor
27321  * Create a new menu button
27322  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27323  * @param {Object} config The config object
27324  */
27325 Roo.SplitButton = function(renderTo, config){
27326     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27327     /**
27328      * @event arrowclick
27329      * Fires when this button's arrow is clicked
27330      * @param {SplitButton} this
27331      * @param {EventObject} e The click event
27332      */
27333     this.addEvents({"arrowclick":true});
27334 };
27335
27336 Roo.extend(Roo.SplitButton, Roo.Button, {
27337     render : function(renderTo){
27338         // this is one sweet looking template!
27339         var tpl = new Roo.Template(
27340             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27341             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27342             '<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>',
27343             "</tbody></table></td><td>",
27344             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27345             '<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>',
27346             "</tbody></table></td></tr></table>"
27347         );
27348         var btn = tpl.append(renderTo, [this.text, this.type], true);
27349         var btnEl = btn.child("button");
27350         if(this.cls){
27351             btn.addClass(this.cls);
27352         }
27353         if(this.icon){
27354             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27355         }
27356         if(this.iconCls){
27357             btnEl.addClass(this.iconCls);
27358             if(!this.cls){
27359                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27360             }
27361         }
27362         this.el = btn;
27363         if(this.handleMouseEvents){
27364             btn.on("mouseover", this.onMouseOver, this);
27365             btn.on("mouseout", this.onMouseOut, this);
27366             btn.on("mousedown", this.onMouseDown, this);
27367             btn.on("mouseup", this.onMouseUp, this);
27368         }
27369         btn.on(this.clickEvent, this.onClick, this);
27370         if(this.tooltip){
27371             if(typeof this.tooltip == 'object'){
27372                 Roo.QuickTips.tips(Roo.apply({
27373                       target: btnEl.id
27374                 }, this.tooltip));
27375             } else {
27376                 btnEl.dom[this.tooltipType] = this.tooltip;
27377             }
27378         }
27379         if(this.arrowTooltip){
27380             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27381         }
27382         if(this.hidden){
27383             this.hide();
27384         }
27385         if(this.disabled){
27386             this.disable();
27387         }
27388         if(this.pressed){
27389             this.el.addClass("x-btn-pressed");
27390         }
27391         if(Roo.isIE && !Roo.isIE7){
27392             this.autoWidth.defer(1, this);
27393         }else{
27394             this.autoWidth();
27395         }
27396         if(this.menu){
27397             this.menu.on("show", this.onMenuShow, this);
27398             this.menu.on("hide", this.onMenuHide, this);
27399         }
27400         this.fireEvent('render', this);
27401     },
27402
27403     // private
27404     autoWidth : function(){
27405         if(this.el){
27406             var tbl = this.el.child("table:first");
27407             var tbl2 = this.el.child("table:last");
27408             this.el.setWidth("auto");
27409             tbl.setWidth("auto");
27410             if(Roo.isIE7 && Roo.isStrict){
27411                 var ib = this.el.child('button:first');
27412                 if(ib && ib.getWidth() > 20){
27413                     ib.clip();
27414                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27415                 }
27416             }
27417             if(this.minWidth){
27418                 if(this.hidden){
27419                     this.el.beginMeasure();
27420                 }
27421                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27422                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27423                 }
27424                 if(this.hidden){
27425                     this.el.endMeasure();
27426                 }
27427             }
27428             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27429         } 
27430     },
27431     /**
27432      * Sets this button's click handler
27433      * @param {Function} handler The function to call when the button is clicked
27434      * @param {Object} scope (optional) Scope for the function passed above
27435      */
27436     setHandler : function(handler, scope){
27437         this.handler = handler;
27438         this.scope = scope;  
27439     },
27440     
27441     /**
27442      * Sets this button's arrow click handler
27443      * @param {Function} handler The function to call when the arrow is clicked
27444      * @param {Object} scope (optional) Scope for the function passed above
27445      */
27446     setArrowHandler : function(handler, scope){
27447         this.arrowHandler = handler;
27448         this.scope = scope;  
27449     },
27450     
27451     /**
27452      * Focus the button
27453      */
27454     focus : function(){
27455         if(this.el){
27456             this.el.child("button:first").focus();
27457         }
27458     },
27459
27460     // private
27461     onClick : function(e){
27462         e.preventDefault();
27463         if(!this.disabled){
27464             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27465                 if(this.menu && !this.menu.isVisible()){
27466                     this.menu.show(this.el, this.menuAlign);
27467                 }
27468                 this.fireEvent("arrowclick", this, e);
27469                 if(this.arrowHandler){
27470                     this.arrowHandler.call(this.scope || this, this, e);
27471                 }
27472             }else{
27473                 this.fireEvent("click", this, e);
27474                 if(this.handler){
27475                     this.handler.call(this.scope || this, this, e);
27476                 }
27477             }
27478         }
27479     },
27480     // private
27481     onMouseDown : function(e){
27482         if(!this.disabled){
27483             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27484         }
27485     },
27486     // private
27487     onMouseUp : function(e){
27488         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27489     }   
27490 });
27491
27492
27493 // backwards compat
27494 Roo.MenuButton = Roo.SplitButton;/*
27495  * Based on:
27496  * Ext JS Library 1.1.1
27497  * Copyright(c) 2006-2007, Ext JS, LLC.
27498  *
27499  * Originally Released Under LGPL - original licence link has changed is not relivant.
27500  *
27501  * Fork - LGPL
27502  * <script type="text/javascript">
27503  */
27504
27505 /**
27506  * @class Roo.Toolbar
27507  * Basic Toolbar class.
27508  * @constructor
27509  * Creates a new Toolbar
27510  * @param {Object} container The config object
27511  */ 
27512 Roo.Toolbar = function(container, buttons, config)
27513 {
27514     /// old consturctor format still supported..
27515     if(container instanceof Array){ // omit the container for later rendering
27516         buttons = container;
27517         config = buttons;
27518         container = null;
27519     }
27520     if (typeof(container) == 'object' && container.xtype) {
27521         config = container;
27522         container = config.container;
27523         buttons = config.buttons || []; // not really - use items!!
27524     }
27525     var xitems = [];
27526     if (config && config.items) {
27527         xitems = config.items;
27528         delete config.items;
27529     }
27530     Roo.apply(this, config);
27531     this.buttons = buttons;
27532     
27533     if(container){
27534         this.render(container);
27535     }
27536     this.xitems = xitems;
27537     Roo.each(xitems, function(b) {
27538         this.add(b);
27539     }, this);
27540     
27541 };
27542
27543 Roo.Toolbar.prototype = {
27544     /**
27545      * @cfg {Array} items
27546      * array of button configs or elements to add (will be converted to a MixedCollection)
27547      */
27548     
27549     /**
27550      * @cfg {String/HTMLElement/Element} container
27551      * The id or element that will contain the toolbar
27552      */
27553     // private
27554     render : function(ct){
27555         this.el = Roo.get(ct);
27556         if(this.cls){
27557             this.el.addClass(this.cls);
27558         }
27559         // using a table allows for vertical alignment
27560         // 100% width is needed by Safari...
27561         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
27562         this.tr = this.el.child("tr", true);
27563         var autoId = 0;
27564         this.items = new Roo.util.MixedCollection(false, function(o){
27565             return o.id || ("item" + (++autoId));
27566         });
27567         if(this.buttons){
27568             this.add.apply(this, this.buttons);
27569             delete this.buttons;
27570         }
27571     },
27572
27573     /**
27574      * Adds element(s) to the toolbar -- this function takes a variable number of 
27575      * arguments of mixed type and adds them to the toolbar.
27576      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
27577      * <ul>
27578      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
27579      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
27580      * <li>Field: Any form field (equivalent to {@link #addField})</li>
27581      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
27582      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
27583      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
27584      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
27585      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
27586      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
27587      * </ul>
27588      * @param {Mixed} arg2
27589      * @param {Mixed} etc.
27590      */
27591     add : function(){
27592         var a = arguments, l = a.length;
27593         for(var i = 0; i < l; i++){
27594             this._add(a[i]);
27595         }
27596     },
27597     // private..
27598     _add : function(el) {
27599         
27600         if (el.xtype) {
27601             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
27602         }
27603         
27604         if (el.applyTo){ // some kind of form field
27605             return this.addField(el);
27606         } 
27607         if (el.render){ // some kind of Toolbar.Item
27608             return this.addItem(el);
27609         }
27610         if (typeof el == "string"){ // string
27611             if(el == "separator" || el == "-"){
27612                 return this.addSeparator();
27613             }
27614             if (el == " "){
27615                 return this.addSpacer();
27616             }
27617             if(el == "->"){
27618                 return this.addFill();
27619             }
27620             return this.addText(el);
27621             
27622         }
27623         if(el.tagName){ // element
27624             return this.addElement(el);
27625         }
27626         if(typeof el == "object"){ // must be button config?
27627             return this.addButton(el);
27628         }
27629         // and now what?!?!
27630         return false;
27631         
27632     },
27633     
27634     /**
27635      * Add an Xtype element
27636      * @param {Object} xtype Xtype Object
27637      * @return {Object} created Object
27638      */
27639     addxtype : function(e){
27640         return this.add(e);  
27641     },
27642     
27643     /**
27644      * Returns the Element for this toolbar.
27645      * @return {Roo.Element}
27646      */
27647     getEl : function(){
27648         return this.el;  
27649     },
27650     
27651     /**
27652      * Adds a separator
27653      * @return {Roo.Toolbar.Item} The separator item
27654      */
27655     addSeparator : function(){
27656         return this.addItem(new Roo.Toolbar.Separator());
27657     },
27658
27659     /**
27660      * Adds a spacer element
27661      * @return {Roo.Toolbar.Spacer} The spacer item
27662      */
27663     addSpacer : function(){
27664         return this.addItem(new Roo.Toolbar.Spacer());
27665     },
27666
27667     /**
27668      * Adds a fill element that forces subsequent additions to the right side of the toolbar
27669      * @return {Roo.Toolbar.Fill} The fill item
27670      */
27671     addFill : function(){
27672         return this.addItem(new Roo.Toolbar.Fill());
27673     },
27674
27675     /**
27676      * Adds any standard HTML element to the toolbar
27677      * @param {String/HTMLElement/Element} el The element or id of the element to add
27678      * @return {Roo.Toolbar.Item} The element's item
27679      */
27680     addElement : function(el){
27681         return this.addItem(new Roo.Toolbar.Item(el));
27682     },
27683     /**
27684      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
27685      * @type Roo.util.MixedCollection  
27686      */
27687     items : false,
27688      
27689     /**
27690      * Adds any Toolbar.Item or subclass
27691      * @param {Roo.Toolbar.Item} item
27692      * @return {Roo.Toolbar.Item} The item
27693      */
27694     addItem : function(item){
27695         var td = this.nextBlock();
27696         item.render(td);
27697         this.items.add(item);
27698         return item;
27699     },
27700     
27701     /**
27702      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
27703      * @param {Object/Array} config A button config or array of configs
27704      * @return {Roo.Toolbar.Button/Array}
27705      */
27706     addButton : function(config){
27707         if(config instanceof Array){
27708             var buttons = [];
27709             for(var i = 0, len = config.length; i < len; i++) {
27710                 buttons.push(this.addButton(config[i]));
27711             }
27712             return buttons;
27713         }
27714         var b = config;
27715         if(!(config instanceof Roo.Toolbar.Button)){
27716             b = config.split ?
27717                 new Roo.Toolbar.SplitButton(config) :
27718                 new Roo.Toolbar.Button(config);
27719         }
27720         var td = this.nextBlock();
27721         b.render(td);
27722         this.items.add(b);
27723         return b;
27724     },
27725     
27726     /**
27727      * Adds text to the toolbar
27728      * @param {String} text The text to add
27729      * @return {Roo.Toolbar.Item} The element's item
27730      */
27731     addText : function(text){
27732         return this.addItem(new Roo.Toolbar.TextItem(text));
27733     },
27734     
27735     /**
27736      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27737      * @param {Number} index The index where the item is to be inserted
27738      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27739      * @return {Roo.Toolbar.Button/Item}
27740      */
27741     insertButton : function(index, item){
27742         if(item instanceof Array){
27743             var buttons = [];
27744             for(var i = 0, len = item.length; i < len; i++) {
27745                buttons.push(this.insertButton(index + i, item[i]));
27746             }
27747             return buttons;
27748         }
27749         if (!(item instanceof Roo.Toolbar.Button)){
27750            item = new Roo.Toolbar.Button(item);
27751         }
27752         var td = document.createElement("td");
27753         this.tr.insertBefore(td, this.tr.childNodes[index]);
27754         item.render(td);
27755         this.items.insert(index, item);
27756         return item;
27757     },
27758     
27759     /**
27760      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27761      * @param {Object} config
27762      * @return {Roo.Toolbar.Item} The element's item
27763      */
27764     addDom : function(config, returnEl){
27765         var td = this.nextBlock();
27766         Roo.DomHelper.overwrite(td, config);
27767         var ti = new Roo.Toolbar.Item(td.firstChild);
27768         ti.render(td);
27769         this.items.add(ti);
27770         return ti;
27771     },
27772
27773     /**
27774      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27775      * @type Roo.util.MixedCollection  
27776      */
27777     fields : false,
27778     
27779     /**
27780      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27781      * Note: the field should not have been rendered yet. For a field that has already been
27782      * rendered, use {@link #addElement}.
27783      * @param {Roo.form.Field} field
27784      * @return {Roo.ToolbarItem}
27785      */
27786      
27787       
27788     addField : function(field) {
27789         if (!this.fields) {
27790             var autoId = 0;
27791             this.fields = new Roo.util.MixedCollection(false, function(o){
27792                 return o.id || ("item" + (++autoId));
27793             });
27794
27795         }
27796         
27797         var td = this.nextBlock();
27798         field.render(td);
27799         var ti = new Roo.Toolbar.Item(td.firstChild);
27800         ti.render(td);
27801         this.items.add(ti);
27802         this.fields.add(field);
27803         return ti;
27804     },
27805     /**
27806      * Hide the toolbar
27807      * @method hide
27808      */
27809      
27810       
27811     hide : function()
27812     {
27813         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27814         this.el.child('div').hide();
27815     },
27816     /**
27817      * Show the toolbar
27818      * @method show
27819      */
27820     show : function()
27821     {
27822         this.el.child('div').show();
27823     },
27824       
27825     // private
27826     nextBlock : function(){
27827         var td = document.createElement("td");
27828         this.tr.appendChild(td);
27829         return td;
27830     },
27831
27832     // private
27833     destroy : function(){
27834         if(this.items){ // rendered?
27835             Roo.destroy.apply(Roo, this.items.items);
27836         }
27837         if(this.fields){ // rendered?
27838             Roo.destroy.apply(Roo, this.fields.items);
27839         }
27840         Roo.Element.uncache(this.el, this.tr);
27841     }
27842 };
27843
27844 /**
27845  * @class Roo.Toolbar.Item
27846  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27847  * @constructor
27848  * Creates a new Item
27849  * @param {HTMLElement} el 
27850  */
27851 Roo.Toolbar.Item = function(el){
27852     this.el = Roo.getDom(el);
27853     this.id = Roo.id(this.el);
27854     this.hidden = false;
27855 };
27856
27857 Roo.Toolbar.Item.prototype = {
27858     
27859     /**
27860      * Get this item's HTML Element
27861      * @return {HTMLElement}
27862      */
27863     getEl : function(){
27864        return this.el;  
27865     },
27866
27867     // private
27868     render : function(td){
27869         this.td = td;
27870         td.appendChild(this.el);
27871     },
27872     
27873     /**
27874      * Removes and destroys this item.
27875      */
27876     destroy : function(){
27877         this.td.parentNode.removeChild(this.td);
27878     },
27879     
27880     /**
27881      * Shows this item.
27882      */
27883     show: function(){
27884         this.hidden = false;
27885         this.td.style.display = "";
27886     },
27887     
27888     /**
27889      * Hides this item.
27890      */
27891     hide: function(){
27892         this.hidden = true;
27893         this.td.style.display = "none";
27894     },
27895     
27896     /**
27897      * Convenience function for boolean show/hide.
27898      * @param {Boolean} visible true to show/false to hide
27899      */
27900     setVisible: function(visible){
27901         if(visible) {
27902             this.show();
27903         }else{
27904             this.hide();
27905         }
27906     },
27907     
27908     /**
27909      * Try to focus this item.
27910      */
27911     focus : function(){
27912         Roo.fly(this.el).focus();
27913     },
27914     
27915     /**
27916      * Disables this item.
27917      */
27918     disable : function(){
27919         Roo.fly(this.td).addClass("x-item-disabled");
27920         this.disabled = true;
27921         this.el.disabled = true;
27922     },
27923     
27924     /**
27925      * Enables this item.
27926      */
27927     enable : function(){
27928         Roo.fly(this.td).removeClass("x-item-disabled");
27929         this.disabled = false;
27930         this.el.disabled = false;
27931     }
27932 };
27933
27934
27935 /**
27936  * @class Roo.Toolbar.Separator
27937  * @extends Roo.Toolbar.Item
27938  * A simple toolbar separator class
27939  * @constructor
27940  * Creates a new Separator
27941  */
27942 Roo.Toolbar.Separator = function(){
27943     var s = document.createElement("span");
27944     s.className = "ytb-sep";
27945     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27946 };
27947 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27948     enable:Roo.emptyFn,
27949     disable:Roo.emptyFn,
27950     focus:Roo.emptyFn
27951 });
27952
27953 /**
27954  * @class Roo.Toolbar.Spacer
27955  * @extends Roo.Toolbar.Item
27956  * A simple element that adds extra horizontal space to a toolbar.
27957  * @constructor
27958  * Creates a new Spacer
27959  */
27960 Roo.Toolbar.Spacer = function(){
27961     var s = document.createElement("div");
27962     s.className = "ytb-spacer";
27963     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27964 };
27965 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27966     enable:Roo.emptyFn,
27967     disable:Roo.emptyFn,
27968     focus:Roo.emptyFn
27969 });
27970
27971 /**
27972  * @class Roo.Toolbar.Fill
27973  * @extends Roo.Toolbar.Spacer
27974  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27975  * @constructor
27976  * Creates a new Spacer
27977  */
27978 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27979     // private
27980     render : function(td){
27981         td.style.width = '100%';
27982         Roo.Toolbar.Fill.superclass.render.call(this, td);
27983     }
27984 });
27985
27986 /**
27987  * @class Roo.Toolbar.TextItem
27988  * @extends Roo.Toolbar.Item
27989  * A simple class that renders text directly into a toolbar.
27990  * @constructor
27991  * Creates a new TextItem
27992  * @param {String} text
27993  */
27994 Roo.Toolbar.TextItem = function(text){
27995     if (typeof(text) == 'object') {
27996         text = text.text;
27997     }
27998     var s = document.createElement("span");
27999     s.className = "ytb-text";
28000     s.innerHTML = text;
28001     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28002 };
28003 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28004     enable:Roo.emptyFn,
28005     disable:Roo.emptyFn,
28006     focus:Roo.emptyFn
28007 });
28008
28009 /**
28010  * @class Roo.Toolbar.Button
28011  * @extends Roo.Button
28012  * A button that renders into a toolbar.
28013  * @constructor
28014  * Creates a new Button
28015  * @param {Object} config A standard {@link Roo.Button} config object
28016  */
28017 Roo.Toolbar.Button = function(config){
28018     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28019 };
28020 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28021     render : function(td){
28022         this.td = td;
28023         Roo.Toolbar.Button.superclass.render.call(this, td);
28024     },
28025     
28026     /**
28027      * Removes and destroys this button
28028      */
28029     destroy : function(){
28030         Roo.Toolbar.Button.superclass.destroy.call(this);
28031         this.td.parentNode.removeChild(this.td);
28032     },
28033     
28034     /**
28035      * Shows this button
28036      */
28037     show: function(){
28038         this.hidden = false;
28039         this.td.style.display = "";
28040     },
28041     
28042     /**
28043      * Hides this button
28044      */
28045     hide: function(){
28046         this.hidden = true;
28047         this.td.style.display = "none";
28048     },
28049
28050     /**
28051      * Disables this item
28052      */
28053     disable : function(){
28054         Roo.fly(this.td).addClass("x-item-disabled");
28055         this.disabled = true;
28056     },
28057
28058     /**
28059      * Enables this item
28060      */
28061     enable : function(){
28062         Roo.fly(this.td).removeClass("x-item-disabled");
28063         this.disabled = false;
28064     }
28065 });
28066 // backwards compat
28067 Roo.ToolbarButton = Roo.Toolbar.Button;
28068
28069 /**
28070  * @class Roo.Toolbar.SplitButton
28071  * @extends Roo.SplitButton
28072  * A menu button that renders into a toolbar.
28073  * @constructor
28074  * Creates a new SplitButton
28075  * @param {Object} config A standard {@link Roo.SplitButton} config object
28076  */
28077 Roo.Toolbar.SplitButton = function(config){
28078     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28079 };
28080 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28081     render : function(td){
28082         this.td = td;
28083         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28084     },
28085     
28086     /**
28087      * Removes and destroys this button
28088      */
28089     destroy : function(){
28090         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28091         this.td.parentNode.removeChild(this.td);
28092     },
28093     
28094     /**
28095      * Shows this button
28096      */
28097     show: function(){
28098         this.hidden = false;
28099         this.td.style.display = "";
28100     },
28101     
28102     /**
28103      * Hides this button
28104      */
28105     hide: function(){
28106         this.hidden = true;
28107         this.td.style.display = "none";
28108     }
28109 });
28110
28111 // backwards compat
28112 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28113  * Based on:
28114  * Ext JS Library 1.1.1
28115  * Copyright(c) 2006-2007, Ext JS, LLC.
28116  *
28117  * Originally Released Under LGPL - original licence link has changed is not relivant.
28118  *
28119  * Fork - LGPL
28120  * <script type="text/javascript">
28121  */
28122  
28123 /**
28124  * @class Roo.PagingToolbar
28125  * @extends Roo.Toolbar
28126  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28127  * @constructor
28128  * Create a new PagingToolbar
28129  * @param {Object} config The config object
28130  */
28131 Roo.PagingToolbar = function(el, ds, config)
28132 {
28133     // old args format still supported... - xtype is prefered..
28134     if (typeof(el) == 'object' && el.xtype) {
28135         // created from xtype...
28136         config = el;
28137         ds = el.dataSource;
28138         el = config.container;
28139     }
28140     var items = [];
28141     if (config.items) {
28142         items = config.items;
28143         config.items = [];
28144     }
28145     
28146     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28147     this.ds = ds;
28148     this.cursor = 0;
28149     this.renderButtons(this.el);
28150     this.bind(ds);
28151     
28152     // supprot items array.
28153    
28154     Roo.each(items, function(e) {
28155         this.add(Roo.factory(e));
28156     },this);
28157     
28158 };
28159
28160 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28161     /**
28162      * @cfg {Roo.data.Store} dataSource
28163      * The underlying data store providing the paged data
28164      */
28165     /**
28166      * @cfg {String/HTMLElement/Element} container
28167      * container The id or element that will contain the toolbar
28168      */
28169     /**
28170      * @cfg {Boolean} displayInfo
28171      * True to display the displayMsg (defaults to false)
28172      */
28173     /**
28174      * @cfg {Number} pageSize
28175      * The number of records to display per page (defaults to 20)
28176      */
28177     pageSize: 20,
28178     /**
28179      * @cfg {String} displayMsg
28180      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28181      */
28182     displayMsg : 'Displaying {0} - {1} of {2}',
28183     /**
28184      * @cfg {String} emptyMsg
28185      * The message to display when no records are found (defaults to "No data to display")
28186      */
28187     emptyMsg : 'No data to display',
28188     /**
28189      * Customizable piece of the default paging text (defaults to "Page")
28190      * @type String
28191      */
28192     beforePageText : "Page",
28193     /**
28194      * Customizable piece of the default paging text (defaults to "of %0")
28195      * @type String
28196      */
28197     afterPageText : "of {0}",
28198     /**
28199      * Customizable piece of the default paging text (defaults to "First Page")
28200      * @type String
28201      */
28202     firstText : "First Page",
28203     /**
28204      * Customizable piece of the default paging text (defaults to "Previous Page")
28205      * @type String
28206      */
28207     prevText : "Previous Page",
28208     /**
28209      * Customizable piece of the default paging text (defaults to "Next Page")
28210      * @type String
28211      */
28212     nextText : "Next Page",
28213     /**
28214      * Customizable piece of the default paging text (defaults to "Last Page")
28215      * @type String
28216      */
28217     lastText : "Last Page",
28218     /**
28219      * Customizable piece of the default paging text (defaults to "Refresh")
28220      * @type String
28221      */
28222     refreshText : "Refresh",
28223
28224     // private
28225     renderButtons : function(el){
28226         Roo.PagingToolbar.superclass.render.call(this, el);
28227         this.first = this.addButton({
28228             tooltip: this.firstText,
28229             cls: "x-btn-icon x-grid-page-first",
28230             disabled: true,
28231             handler: this.onClick.createDelegate(this, ["first"])
28232         });
28233         this.prev = this.addButton({
28234             tooltip: this.prevText,
28235             cls: "x-btn-icon x-grid-page-prev",
28236             disabled: true,
28237             handler: this.onClick.createDelegate(this, ["prev"])
28238         });
28239         //this.addSeparator();
28240         this.add(this.beforePageText);
28241         this.field = Roo.get(this.addDom({
28242            tag: "input",
28243            type: "text",
28244            size: "3",
28245            value: "1",
28246            cls: "x-grid-page-number"
28247         }).el);
28248         this.field.on("keydown", this.onPagingKeydown, this);
28249         this.field.on("focus", function(){this.dom.select();});
28250         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28251         this.field.setHeight(18);
28252         //this.addSeparator();
28253         this.next = this.addButton({
28254             tooltip: this.nextText,
28255             cls: "x-btn-icon x-grid-page-next",
28256             disabled: true,
28257             handler: this.onClick.createDelegate(this, ["next"])
28258         });
28259         this.last = this.addButton({
28260             tooltip: this.lastText,
28261             cls: "x-btn-icon x-grid-page-last",
28262             disabled: true,
28263             handler: this.onClick.createDelegate(this, ["last"])
28264         });
28265         //this.addSeparator();
28266         this.loading = this.addButton({
28267             tooltip: this.refreshText,
28268             cls: "x-btn-icon x-grid-loading",
28269             handler: this.onClick.createDelegate(this, ["refresh"])
28270         });
28271
28272         if(this.displayInfo){
28273             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28274         }
28275     },
28276
28277     // private
28278     updateInfo : function(){
28279         if(this.displayEl){
28280             var count = this.ds.getCount();
28281             var msg = count == 0 ?
28282                 this.emptyMsg :
28283                 String.format(
28284                     this.displayMsg,
28285                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28286                 );
28287             this.displayEl.update(msg);
28288         }
28289     },
28290
28291     // private
28292     onLoad : function(ds, r, o){
28293        this.cursor = o.params ? o.params.start : 0;
28294        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28295
28296        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28297        this.field.dom.value = ap;
28298        this.first.setDisabled(ap == 1);
28299        this.prev.setDisabled(ap == 1);
28300        this.next.setDisabled(ap == ps);
28301        this.last.setDisabled(ap == ps);
28302        this.loading.enable();
28303        this.updateInfo();
28304     },
28305
28306     // private
28307     getPageData : function(){
28308         var total = this.ds.getTotalCount();
28309         return {
28310             total : total,
28311             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28312             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28313         };
28314     },
28315
28316     // private
28317     onLoadError : function(){
28318         this.loading.enable();
28319     },
28320
28321     // private
28322     onPagingKeydown : function(e){
28323         var k = e.getKey();
28324         var d = this.getPageData();
28325         if(k == e.RETURN){
28326             var v = this.field.dom.value, pageNum;
28327             if(!v || isNaN(pageNum = parseInt(v, 10))){
28328                 this.field.dom.value = d.activePage;
28329                 return;
28330             }
28331             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28332             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28333             e.stopEvent();
28334         }
28335         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))
28336         {
28337           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28338           this.field.dom.value = pageNum;
28339           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28340           e.stopEvent();
28341         }
28342         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28343         {
28344           var v = this.field.dom.value, pageNum; 
28345           var increment = (e.shiftKey) ? 10 : 1;
28346           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28347             increment *= -1;
28348           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28349             this.field.dom.value = d.activePage;
28350             return;
28351           }
28352           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28353           {
28354             this.field.dom.value = parseInt(v, 10) + increment;
28355             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28356             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28357           }
28358           e.stopEvent();
28359         }
28360     },
28361
28362     // private
28363     beforeLoad : function(){
28364         if(this.loading){
28365             this.loading.disable();
28366         }
28367     },
28368
28369     // private
28370     onClick : function(which){
28371         var ds = this.ds;
28372         switch(which){
28373             case "first":
28374                 ds.load({params:{start: 0, limit: this.pageSize}});
28375             break;
28376             case "prev":
28377                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28378             break;
28379             case "next":
28380                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28381             break;
28382             case "last":
28383                 var total = ds.getTotalCount();
28384                 var extra = total % this.pageSize;
28385                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28386                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28387             break;
28388             case "refresh":
28389                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28390             break;
28391         }
28392     },
28393
28394     /**
28395      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28396      * @param {Roo.data.Store} store The data store to unbind
28397      */
28398     unbind : function(ds){
28399         ds.un("beforeload", this.beforeLoad, this);
28400         ds.un("load", this.onLoad, this);
28401         ds.un("loadexception", this.onLoadError, this);
28402         ds.un("remove", this.updateInfo, this);
28403         ds.un("add", this.updateInfo, this);
28404         this.ds = undefined;
28405     },
28406
28407     /**
28408      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28409      * @param {Roo.data.Store} store The data store to bind
28410      */
28411     bind : function(ds){
28412         ds.on("beforeload", this.beforeLoad, this);
28413         ds.on("load", this.onLoad, this);
28414         ds.on("loadexception", this.onLoadError, this);
28415         ds.on("remove", this.updateInfo, this);
28416         ds.on("add", this.updateInfo, this);
28417         this.ds = ds;
28418     }
28419 });/*
28420  * Based on:
28421  * Ext JS Library 1.1.1
28422  * Copyright(c) 2006-2007, Ext JS, LLC.
28423  *
28424  * Originally Released Under LGPL - original licence link has changed is not relivant.
28425  *
28426  * Fork - LGPL
28427  * <script type="text/javascript">
28428  */
28429
28430 /**
28431  * @class Roo.Resizable
28432  * @extends Roo.util.Observable
28433  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28434  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28435  * 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
28436  * the element will be wrapped for you automatically.</p>
28437  * <p>Here is the list of valid resize handles:</p>
28438  * <pre>
28439 Value   Description
28440 ------  -------------------
28441  'n'     north
28442  's'     south
28443  'e'     east
28444  'w'     west
28445  'nw'    northwest
28446  'sw'    southwest
28447  'se'    southeast
28448  'ne'    northeast
28449  'hd'    horizontal drag
28450  'all'   all
28451 </pre>
28452  * <p>Here's an example showing the creation of a typical Resizable:</p>
28453  * <pre><code>
28454 var resizer = new Roo.Resizable("element-id", {
28455     handles: 'all',
28456     minWidth: 200,
28457     minHeight: 100,
28458     maxWidth: 500,
28459     maxHeight: 400,
28460     pinned: true
28461 });
28462 resizer.on("resize", myHandler);
28463 </code></pre>
28464  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28465  * resizer.east.setDisplayed(false);</p>
28466  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28467  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28468  * resize operation's new size (defaults to [0, 0])
28469  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28470  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28471  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28472  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28473  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28474  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28475  * @cfg {Number} width The width of the element in pixels (defaults to null)
28476  * @cfg {Number} height The height of the element in pixels (defaults to null)
28477  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28478  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28479  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28480  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28481  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28482  * in favor of the handles config option (defaults to false)
28483  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28484  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28485  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28486  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28487  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28488  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28489  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28490  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28491  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28492  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28493  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28494  * @constructor
28495  * Create a new resizable component
28496  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
28497  * @param {Object} config configuration options
28498   */
28499 Roo.Resizable = function(el, config)
28500 {
28501     this.el = Roo.get(el);
28502
28503     if(config && config.wrap){
28504         config.resizeChild = this.el;
28505         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
28506         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
28507         this.el.setStyle("overflow", "hidden");
28508         this.el.setPositioning(config.resizeChild.getPositioning());
28509         config.resizeChild.clearPositioning();
28510         if(!config.width || !config.height){
28511             var csize = config.resizeChild.getSize();
28512             this.el.setSize(csize.width, csize.height);
28513         }
28514         if(config.pinned && !config.adjustments){
28515             config.adjustments = "auto";
28516         }
28517     }
28518
28519     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
28520     this.proxy.unselectable();
28521     this.proxy.enableDisplayMode('block');
28522
28523     Roo.apply(this, config);
28524
28525     if(this.pinned){
28526         this.disableTrackOver = true;
28527         this.el.addClass("x-resizable-pinned");
28528     }
28529     // if the element isn't positioned, make it relative
28530     var position = this.el.getStyle("position");
28531     if(position != "absolute" && position != "fixed"){
28532         this.el.setStyle("position", "relative");
28533     }
28534     if(!this.handles){ // no handles passed, must be legacy style
28535         this.handles = 's,e,se';
28536         if(this.multiDirectional){
28537             this.handles += ',n,w';
28538         }
28539     }
28540     if(this.handles == "all"){
28541         this.handles = "n s e w ne nw se sw";
28542     }
28543     var hs = this.handles.split(/\s*?[,;]\s*?| /);
28544     var ps = Roo.Resizable.positions;
28545     for(var i = 0, len = hs.length; i < len; i++){
28546         if(hs[i] && ps[hs[i]]){
28547             var pos = ps[hs[i]];
28548             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
28549         }
28550     }
28551     // legacy
28552     this.corner = this.southeast;
28553     
28554     // updateBox = the box can move..
28555     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
28556         this.updateBox = true;
28557     }
28558
28559     this.activeHandle = null;
28560
28561     if(this.resizeChild){
28562         if(typeof this.resizeChild == "boolean"){
28563             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
28564         }else{
28565             this.resizeChild = Roo.get(this.resizeChild, true);
28566         }
28567     }
28568     
28569     if(this.adjustments == "auto"){
28570         var rc = this.resizeChild;
28571         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
28572         if(rc && (hw || hn)){
28573             rc.position("relative");
28574             rc.setLeft(hw ? hw.el.getWidth() : 0);
28575             rc.setTop(hn ? hn.el.getHeight() : 0);
28576         }
28577         this.adjustments = [
28578             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
28579             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
28580         ];
28581     }
28582
28583     if(this.draggable){
28584         this.dd = this.dynamic ?
28585             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
28586         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
28587     }
28588
28589     // public events
28590     this.addEvents({
28591         /**
28592          * @event beforeresize
28593          * Fired before resize is allowed. Set enabled to false to cancel resize.
28594          * @param {Roo.Resizable} this
28595          * @param {Roo.EventObject} e The mousedown event
28596          */
28597         "beforeresize" : true,
28598         /**
28599          * @event resize
28600          * Fired after a resize.
28601          * @param {Roo.Resizable} this
28602          * @param {Number} width The new width
28603          * @param {Number} height The new height
28604          * @param {Roo.EventObject} e The mouseup event
28605          */
28606         "resize" : true
28607     });
28608
28609     if(this.width !== null && this.height !== null){
28610         this.resizeTo(this.width, this.height);
28611     }else{
28612         this.updateChildSize();
28613     }
28614     if(Roo.isIE){
28615         this.el.dom.style.zoom = 1;
28616     }
28617     Roo.Resizable.superclass.constructor.call(this);
28618 };
28619
28620 Roo.extend(Roo.Resizable, Roo.util.Observable, {
28621         resizeChild : false,
28622         adjustments : [0, 0],
28623         minWidth : 5,
28624         minHeight : 5,
28625         maxWidth : 10000,
28626         maxHeight : 10000,
28627         enabled : true,
28628         animate : false,
28629         duration : .35,
28630         dynamic : false,
28631         handles : false,
28632         multiDirectional : false,
28633         disableTrackOver : false,
28634         easing : 'easeOutStrong',
28635         widthIncrement : 0,
28636         heightIncrement : 0,
28637         pinned : false,
28638         width : null,
28639         height : null,
28640         preserveRatio : false,
28641         transparent: false,
28642         minX: 0,
28643         minY: 0,
28644         draggable: false,
28645
28646         /**
28647          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
28648          */
28649         constrainTo: undefined,
28650         /**
28651          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
28652          */
28653         resizeRegion: undefined,
28654
28655
28656     /**
28657      * Perform a manual resize
28658      * @param {Number} width
28659      * @param {Number} height
28660      */
28661     resizeTo : function(width, height){
28662         this.el.setSize(width, height);
28663         this.updateChildSize();
28664         this.fireEvent("resize", this, width, height, null);
28665     },
28666
28667     // private
28668     startSizing : function(e, handle){
28669         this.fireEvent("beforeresize", this, e);
28670         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
28671
28672             if(!this.overlay){
28673                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
28674                 this.overlay.unselectable();
28675                 this.overlay.enableDisplayMode("block");
28676                 this.overlay.on("mousemove", this.onMouseMove, this);
28677                 this.overlay.on("mouseup", this.onMouseUp, this);
28678             }
28679             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
28680
28681             this.resizing = true;
28682             this.startBox = this.el.getBox();
28683             this.startPoint = e.getXY();
28684             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
28685                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
28686
28687             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28688             this.overlay.show();
28689
28690             if(this.constrainTo) {
28691                 var ct = Roo.get(this.constrainTo);
28692                 this.resizeRegion = ct.getRegion().adjust(
28693                     ct.getFrameWidth('t'),
28694                     ct.getFrameWidth('l'),
28695                     -ct.getFrameWidth('b'),
28696                     -ct.getFrameWidth('r')
28697                 );
28698             }
28699
28700             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
28701             this.proxy.show();
28702             this.proxy.setBox(this.startBox);
28703             if(!this.dynamic){
28704                 this.proxy.setStyle('visibility', 'visible');
28705             }
28706         }
28707     },
28708
28709     // private
28710     onMouseDown : function(handle, e){
28711         if(this.enabled){
28712             e.stopEvent();
28713             this.activeHandle = handle;
28714             this.startSizing(e, handle);
28715         }
28716     },
28717
28718     // private
28719     onMouseUp : function(e){
28720         var size = this.resizeElement();
28721         this.resizing = false;
28722         this.handleOut();
28723         this.overlay.hide();
28724         this.proxy.hide();
28725         this.fireEvent("resize", this, size.width, size.height, e);
28726     },
28727
28728     // private
28729     updateChildSize : function(){
28730         
28731         if(this.resizeChild){
28732             var el = this.el;
28733             var child = this.resizeChild;
28734             var adj = this.adjustments;
28735             if(el.dom.offsetWidth){
28736                 var b = el.getSize(true);
28737                 child.setSize(b.width+adj[0], b.height+adj[1]);
28738             }
28739             // Second call here for IE
28740             // The first call enables instant resizing and
28741             // the second call corrects scroll bars if they
28742             // exist
28743             if(Roo.isIE){
28744                 setTimeout(function(){
28745                     if(el.dom.offsetWidth){
28746                         var b = el.getSize(true);
28747                         child.setSize(b.width+adj[0], b.height+adj[1]);
28748                     }
28749                 }, 10);
28750             }
28751         }
28752     },
28753
28754     // private
28755     snap : function(value, inc, min){
28756         if(!inc || !value) return value;
28757         var newValue = value;
28758         var m = value % inc;
28759         if(m > 0){
28760             if(m > (inc/2)){
28761                 newValue = value + (inc-m);
28762             }else{
28763                 newValue = value - m;
28764             }
28765         }
28766         return Math.max(min, newValue);
28767     },
28768
28769     // private
28770     resizeElement : function(){
28771         var box = this.proxy.getBox();
28772         if(this.updateBox){
28773             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28774         }else{
28775             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28776         }
28777         this.updateChildSize();
28778         if(!this.dynamic){
28779             this.proxy.hide();
28780         }
28781         return box;
28782     },
28783
28784     // private
28785     constrain : function(v, diff, m, mx){
28786         if(v - diff < m){
28787             diff = v - m;
28788         }else if(v - diff > mx){
28789             diff = mx - v;
28790         }
28791         return diff;
28792     },
28793
28794     // private
28795     onMouseMove : function(e){
28796         if(this.enabled){
28797             try{// try catch so if something goes wrong the user doesn't get hung
28798
28799             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28800                 return;
28801             }
28802
28803             //var curXY = this.startPoint;
28804             var curSize = this.curSize || this.startBox;
28805             var x = this.startBox.x, y = this.startBox.y;
28806             var ox = x, oy = y;
28807             var w = curSize.width, h = curSize.height;
28808             var ow = w, oh = h;
28809             var mw = this.minWidth, mh = this.minHeight;
28810             var mxw = this.maxWidth, mxh = this.maxHeight;
28811             var wi = this.widthIncrement;
28812             var hi = this.heightIncrement;
28813
28814             var eventXY = e.getXY();
28815             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28816             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28817
28818             var pos = this.activeHandle.position;
28819
28820             switch(pos){
28821                 case "east":
28822                     w += diffX;
28823                     w = Math.min(Math.max(mw, w), mxw);
28824                     break;
28825              
28826                 case "south":
28827                     h += diffY;
28828                     h = Math.min(Math.max(mh, h), mxh);
28829                     break;
28830                 case "southeast":
28831                     w += diffX;
28832                     h += diffY;
28833                     w = Math.min(Math.max(mw, w), mxw);
28834                     h = Math.min(Math.max(mh, h), mxh);
28835                     break;
28836                 case "north":
28837                     diffY = this.constrain(h, diffY, mh, mxh);
28838                     y += diffY;
28839                     h -= diffY;
28840                     break;
28841                 case "hdrag":
28842                     
28843                     if (wi) {
28844                         var adiffX = Math.abs(diffX);
28845                         var sub = (adiffX % wi); // how much 
28846                         if (sub > (wi/2)) { // far enough to snap
28847                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28848                         } else {
28849                             // remove difference.. 
28850                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28851                         }
28852                     }
28853                     x += diffX;
28854                     x = Math.max(this.minX, x);
28855                     break;
28856                 case "west":
28857                     diffX = this.constrain(w, diffX, mw, mxw);
28858                     x += diffX;
28859                     w -= diffX;
28860                     break;
28861                 case "northeast":
28862                     w += diffX;
28863                     w = Math.min(Math.max(mw, w), mxw);
28864                     diffY = this.constrain(h, diffY, mh, mxh);
28865                     y += diffY;
28866                     h -= diffY;
28867                     break;
28868                 case "northwest":
28869                     diffX = this.constrain(w, diffX, mw, mxw);
28870                     diffY = this.constrain(h, diffY, mh, mxh);
28871                     y += diffY;
28872                     h -= diffY;
28873                     x += diffX;
28874                     w -= diffX;
28875                     break;
28876                case "southwest":
28877                     diffX = this.constrain(w, diffX, mw, mxw);
28878                     h += diffY;
28879                     h = Math.min(Math.max(mh, h), mxh);
28880                     x += diffX;
28881                     w -= diffX;
28882                     break;
28883             }
28884
28885             var sw = this.snap(w, wi, mw);
28886             var sh = this.snap(h, hi, mh);
28887             if(sw != w || sh != h){
28888                 switch(pos){
28889                     case "northeast":
28890                         y -= sh - h;
28891                     break;
28892                     case "north":
28893                         y -= sh - h;
28894                         break;
28895                     case "southwest":
28896                         x -= sw - w;
28897                     break;
28898                     case "west":
28899                         x -= sw - w;
28900                         break;
28901                     case "northwest":
28902                         x -= sw - w;
28903                         y -= sh - h;
28904                     break;
28905                 }
28906                 w = sw;
28907                 h = sh;
28908             }
28909
28910             if(this.preserveRatio){
28911                 switch(pos){
28912                     case "southeast":
28913                     case "east":
28914                         h = oh * (w/ow);
28915                         h = Math.min(Math.max(mh, h), mxh);
28916                         w = ow * (h/oh);
28917                        break;
28918                     case "south":
28919                         w = ow * (h/oh);
28920                         w = Math.min(Math.max(mw, w), mxw);
28921                         h = oh * (w/ow);
28922                         break;
28923                     case "northeast":
28924                         w = ow * (h/oh);
28925                         w = Math.min(Math.max(mw, w), mxw);
28926                         h = oh * (w/ow);
28927                     break;
28928                     case "north":
28929                         var tw = w;
28930                         w = ow * (h/oh);
28931                         w = Math.min(Math.max(mw, w), mxw);
28932                         h = oh * (w/ow);
28933                         x += (tw - w) / 2;
28934                         break;
28935                     case "southwest":
28936                         h = oh * (w/ow);
28937                         h = Math.min(Math.max(mh, h), mxh);
28938                         var tw = w;
28939                         w = ow * (h/oh);
28940                         x += tw - w;
28941                         break;
28942                     case "west":
28943                         var th = h;
28944                         h = oh * (w/ow);
28945                         h = Math.min(Math.max(mh, h), mxh);
28946                         y += (th - h) / 2;
28947                         var tw = w;
28948                         w = ow * (h/oh);
28949                         x += tw - w;
28950                        break;
28951                     case "northwest":
28952                         var tw = w;
28953                         var th = h;
28954                         h = oh * (w/ow);
28955                         h = Math.min(Math.max(mh, h), mxh);
28956                         w = ow * (h/oh);
28957                         y += th - h;
28958                         x += tw - w;
28959                        break;
28960
28961                 }
28962             }
28963             if (pos == 'hdrag') {
28964                 w = ow;
28965             }
28966             this.proxy.setBounds(x, y, w, h);
28967             if(this.dynamic){
28968                 this.resizeElement();
28969             }
28970             }catch(e){}
28971         }
28972     },
28973
28974     // private
28975     handleOver : function(){
28976         if(this.enabled){
28977             this.el.addClass("x-resizable-over");
28978         }
28979     },
28980
28981     // private
28982     handleOut : function(){
28983         if(!this.resizing){
28984             this.el.removeClass("x-resizable-over");
28985         }
28986     },
28987
28988     /**
28989      * Returns the element this component is bound to.
28990      * @return {Roo.Element}
28991      */
28992     getEl : function(){
28993         return this.el;
28994     },
28995
28996     /**
28997      * Returns the resizeChild element (or null).
28998      * @return {Roo.Element}
28999      */
29000     getResizeChild : function(){
29001         return this.resizeChild;
29002     },
29003     groupHandler : function()
29004     {
29005         
29006     },
29007     /**
29008      * Destroys this resizable. If the element was wrapped and
29009      * removeEl is not true then the element remains.
29010      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29011      */
29012     destroy : function(removeEl){
29013         this.proxy.remove();
29014         if(this.overlay){
29015             this.overlay.removeAllListeners();
29016             this.overlay.remove();
29017         }
29018         var ps = Roo.Resizable.positions;
29019         for(var k in ps){
29020             if(typeof ps[k] != "function" && this[ps[k]]){
29021                 var h = this[ps[k]];
29022                 h.el.removeAllListeners();
29023                 h.el.remove();
29024             }
29025         }
29026         if(removeEl){
29027             this.el.update("");
29028             this.el.remove();
29029         }
29030     }
29031 });
29032
29033 // private
29034 // hash to map config positions to true positions
29035 Roo.Resizable.positions = {
29036     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29037     hd: "hdrag"
29038 };
29039
29040 // private
29041 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29042     if(!this.tpl){
29043         // only initialize the template if resizable is used
29044         var tpl = Roo.DomHelper.createTemplate(
29045             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29046         );
29047         tpl.compile();
29048         Roo.Resizable.Handle.prototype.tpl = tpl;
29049     }
29050     this.position = pos;
29051     this.rz = rz;
29052     // show north drag fro topdra
29053     var handlepos = pos == 'hdrag' ? 'north' : pos;
29054     
29055     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29056     if (pos == 'hdrag') {
29057         this.el.setStyle('cursor', 'pointer');
29058     }
29059     this.el.unselectable();
29060     if(transparent){
29061         this.el.setOpacity(0);
29062     }
29063     this.el.on("mousedown", this.onMouseDown, this);
29064     if(!disableTrackOver){
29065         this.el.on("mouseover", this.onMouseOver, this);
29066         this.el.on("mouseout", this.onMouseOut, this);
29067     }
29068 };
29069
29070 // private
29071 Roo.Resizable.Handle.prototype = {
29072     afterResize : function(rz){
29073         // do nothing
29074     },
29075     // private
29076     onMouseDown : function(e){
29077         this.rz.onMouseDown(this, e);
29078     },
29079     // private
29080     onMouseOver : function(e){
29081         this.rz.handleOver(this, e);
29082     },
29083     // private
29084     onMouseOut : function(e){
29085         this.rz.handleOut(this, e);
29086     }
29087 };/*
29088  * Based on:
29089  * Ext JS Library 1.1.1
29090  * Copyright(c) 2006-2007, Ext JS, LLC.
29091  *
29092  * Originally Released Under LGPL - original licence link has changed is not relivant.
29093  *
29094  * Fork - LGPL
29095  * <script type="text/javascript">
29096  */
29097
29098 /**
29099  * @class Roo.Editor
29100  * @extends Roo.Component
29101  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29102  * @constructor
29103  * Create a new Editor
29104  * @param {Roo.form.Field} field The Field object (or descendant)
29105  * @param {Object} config The config object
29106  */
29107 Roo.Editor = function(field, config){
29108     Roo.Editor.superclass.constructor.call(this, config);
29109     this.field = field;
29110     this.addEvents({
29111         /**
29112              * @event beforestartedit
29113              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29114              * false from the handler of this event.
29115              * @param {Editor} this
29116              * @param {Roo.Element} boundEl The underlying element bound to this editor
29117              * @param {Mixed} value The field value being set
29118              */
29119         "beforestartedit" : true,
29120         /**
29121              * @event startedit
29122              * Fires when this editor is displayed
29123              * @param {Roo.Element} boundEl The underlying element bound to this editor
29124              * @param {Mixed} value The starting field value
29125              */
29126         "startedit" : true,
29127         /**
29128              * @event beforecomplete
29129              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29130              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29131              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29132              * event will not fire since no edit actually occurred.
29133              * @param {Editor} this
29134              * @param {Mixed} value The current field value
29135              * @param {Mixed} startValue The original field value
29136              */
29137         "beforecomplete" : true,
29138         /**
29139              * @event complete
29140              * Fires after editing is complete and any changed value has been written to the underlying field.
29141              * @param {Editor} this
29142              * @param {Mixed} value The current field value
29143              * @param {Mixed} startValue The original field value
29144              */
29145         "complete" : true,
29146         /**
29147          * @event specialkey
29148          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29149          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29150          * @param {Roo.form.Field} this
29151          * @param {Roo.EventObject} e The event object
29152          */
29153         "specialkey" : true
29154     });
29155 };
29156
29157 Roo.extend(Roo.Editor, Roo.Component, {
29158     /**
29159      * @cfg {Boolean/String} autosize
29160      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29161      * or "height" to adopt the height only (defaults to false)
29162      */
29163     /**
29164      * @cfg {Boolean} revertInvalid
29165      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29166      * validation fails (defaults to true)
29167      */
29168     /**
29169      * @cfg {Boolean} ignoreNoChange
29170      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29171      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29172      * will never be ignored.
29173      */
29174     /**
29175      * @cfg {Boolean} hideEl
29176      * False to keep the bound element visible while the editor is displayed (defaults to true)
29177      */
29178     /**
29179      * @cfg {Mixed} value
29180      * The data value of the underlying field (defaults to "")
29181      */
29182     value : "",
29183     /**
29184      * @cfg {String} alignment
29185      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29186      */
29187     alignment: "c-c?",
29188     /**
29189      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29190      * for bottom-right shadow (defaults to "frame")
29191      */
29192     shadow : "frame",
29193     /**
29194      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29195      */
29196     constrain : false,
29197     /**
29198      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29199      */
29200     completeOnEnter : false,
29201     /**
29202      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29203      */
29204     cancelOnEsc : false,
29205     /**
29206      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29207      */
29208     updateEl : false,
29209
29210     // private
29211     onRender : function(ct, position){
29212         this.el = new Roo.Layer({
29213             shadow: this.shadow,
29214             cls: "x-editor",
29215             parentEl : ct,
29216             shim : this.shim,
29217             shadowOffset:4,
29218             id: this.id,
29219             constrain: this.constrain
29220         });
29221         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29222         if(this.field.msgTarget != 'title'){
29223             this.field.msgTarget = 'qtip';
29224         }
29225         this.field.render(this.el);
29226         if(Roo.isGecko){
29227             this.field.el.dom.setAttribute('autocomplete', 'off');
29228         }
29229         this.field.on("specialkey", this.onSpecialKey, this);
29230         if(this.swallowKeys){
29231             this.field.el.swallowEvent(['keydown','keypress']);
29232         }
29233         this.field.show();
29234         this.field.on("blur", this.onBlur, this);
29235         if(this.field.grow){
29236             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29237         }
29238     },
29239
29240     onSpecialKey : function(field, e)
29241     {
29242         //Roo.log('editor onSpecialKey');
29243         if(this.completeOnEnter && e.getKey() == e.ENTER){
29244             e.stopEvent();
29245             this.completeEdit();
29246             return;
29247         }
29248         // do not fire special key otherwise it might hide close the editor...
29249         if(e.getKey() == e.ENTER){    
29250             return;
29251         }
29252         if(this.cancelOnEsc && e.getKey() == e.ESC){
29253             this.cancelEdit();
29254             return;
29255         } 
29256         this.fireEvent('specialkey', field, e);
29257     
29258     },
29259
29260     /**
29261      * Starts the editing process and shows the editor.
29262      * @param {String/HTMLElement/Element} el The element to edit
29263      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29264       * to the innerHTML of el.
29265      */
29266     startEdit : function(el, value){
29267         if(this.editing){
29268             this.completeEdit();
29269         }
29270         this.boundEl = Roo.get(el);
29271         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29272         if(!this.rendered){
29273             this.render(this.parentEl || document.body);
29274         }
29275         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29276             return;
29277         }
29278         this.startValue = v;
29279         this.field.setValue(v);
29280         if(this.autoSize){
29281             var sz = this.boundEl.getSize();
29282             switch(this.autoSize){
29283                 case "width":
29284                 this.setSize(sz.width,  "");
29285                 break;
29286                 case "height":
29287                 this.setSize("",  sz.height);
29288                 break;
29289                 default:
29290                 this.setSize(sz.width,  sz.height);
29291             }
29292         }
29293         this.el.alignTo(this.boundEl, this.alignment);
29294         this.editing = true;
29295         if(Roo.QuickTips){
29296             Roo.QuickTips.disable();
29297         }
29298         this.show();
29299     },
29300
29301     /**
29302      * Sets the height and width of this editor.
29303      * @param {Number} width The new width
29304      * @param {Number} height The new height
29305      */
29306     setSize : function(w, h){
29307         this.field.setSize(w, h);
29308         if(this.el){
29309             this.el.sync();
29310         }
29311     },
29312
29313     /**
29314      * Realigns the editor to the bound field based on the current alignment config value.
29315      */
29316     realign : function(){
29317         this.el.alignTo(this.boundEl, this.alignment);
29318     },
29319
29320     /**
29321      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29322      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29323      */
29324     completeEdit : function(remainVisible){
29325         if(!this.editing){
29326             return;
29327         }
29328         var v = this.getValue();
29329         if(this.revertInvalid !== false && !this.field.isValid()){
29330             v = this.startValue;
29331             this.cancelEdit(true);
29332         }
29333         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29334             this.editing = false;
29335             this.hide();
29336             return;
29337         }
29338         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29339             this.editing = false;
29340             if(this.updateEl && this.boundEl){
29341                 this.boundEl.update(v);
29342             }
29343             if(remainVisible !== true){
29344                 this.hide();
29345             }
29346             this.fireEvent("complete", this, v, this.startValue);
29347         }
29348     },
29349
29350     // private
29351     onShow : function(){
29352         this.el.show();
29353         if(this.hideEl !== false){
29354             this.boundEl.hide();
29355         }
29356         this.field.show();
29357         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29358             this.fixIEFocus = true;
29359             this.deferredFocus.defer(50, this);
29360         }else{
29361             this.field.focus();
29362         }
29363         this.fireEvent("startedit", this.boundEl, this.startValue);
29364     },
29365
29366     deferredFocus : function(){
29367         if(this.editing){
29368             this.field.focus();
29369         }
29370     },
29371
29372     /**
29373      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29374      * reverted to the original starting value.
29375      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29376      * cancel (defaults to false)
29377      */
29378     cancelEdit : function(remainVisible){
29379         if(this.editing){
29380             this.setValue(this.startValue);
29381             if(remainVisible !== true){
29382                 this.hide();
29383             }
29384         }
29385     },
29386
29387     // private
29388     onBlur : function(){
29389         if(this.allowBlur !== true && this.editing){
29390             this.completeEdit();
29391         }
29392     },
29393
29394     // private
29395     onHide : function(){
29396         if(this.editing){
29397             this.completeEdit();
29398             return;
29399         }
29400         this.field.blur();
29401         if(this.field.collapse){
29402             this.field.collapse();
29403         }
29404         this.el.hide();
29405         if(this.hideEl !== false){
29406             this.boundEl.show();
29407         }
29408         if(Roo.QuickTips){
29409             Roo.QuickTips.enable();
29410         }
29411     },
29412
29413     /**
29414      * Sets the data value of the editor
29415      * @param {Mixed} value Any valid value supported by the underlying field
29416      */
29417     setValue : function(v){
29418         this.field.setValue(v);
29419     },
29420
29421     /**
29422      * Gets the data value of the editor
29423      * @return {Mixed} The data value
29424      */
29425     getValue : function(){
29426         return this.field.getValue();
29427     }
29428 });/*
29429  * Based on:
29430  * Ext JS Library 1.1.1
29431  * Copyright(c) 2006-2007, Ext JS, LLC.
29432  *
29433  * Originally Released Under LGPL - original licence link has changed is not relivant.
29434  *
29435  * Fork - LGPL
29436  * <script type="text/javascript">
29437  */
29438  
29439 /**
29440  * @class Roo.BasicDialog
29441  * @extends Roo.util.Observable
29442  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29443  * <pre><code>
29444 var dlg = new Roo.BasicDialog("my-dlg", {
29445     height: 200,
29446     width: 300,
29447     minHeight: 100,
29448     minWidth: 150,
29449     modal: true,
29450     proxyDrag: true,
29451     shadow: true
29452 });
29453 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29454 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29455 dlg.addButton('Cancel', dlg.hide, dlg);
29456 dlg.show();
29457 </code></pre>
29458   <b>A Dialog should always be a direct child of the body element.</b>
29459  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29460  * @cfg {String} title Default text to display in the title bar (defaults to null)
29461  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29462  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29463  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29464  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29465  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29466  * (defaults to null with no animation)
29467  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29468  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29469  * property for valid values (defaults to 'all')
29470  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29471  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29472  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29473  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29474  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29475  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29476  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29477  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29478  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29479  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29480  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29481  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
29482  * draggable = true (defaults to false)
29483  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
29484  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29485  * shadow (defaults to false)
29486  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
29487  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
29488  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
29489  * @cfg {Array} buttons Array of buttons
29490  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
29491  * @constructor
29492  * Create a new BasicDialog.
29493  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
29494  * @param {Object} config Configuration options
29495  */
29496 Roo.BasicDialog = function(el, config){
29497     this.el = Roo.get(el);
29498     var dh = Roo.DomHelper;
29499     if(!this.el && config && config.autoCreate){
29500         if(typeof config.autoCreate == "object"){
29501             if(!config.autoCreate.id){
29502                 config.autoCreate.id = el;
29503             }
29504             this.el = dh.append(document.body,
29505                         config.autoCreate, true);
29506         }else{
29507             this.el = dh.append(document.body,
29508                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
29509         }
29510     }
29511     el = this.el;
29512     el.setDisplayed(true);
29513     el.hide = this.hideAction;
29514     this.id = el.id;
29515     el.addClass("x-dlg");
29516
29517     Roo.apply(this, config);
29518
29519     this.proxy = el.createProxy("x-dlg-proxy");
29520     this.proxy.hide = this.hideAction;
29521     this.proxy.setOpacity(.5);
29522     this.proxy.hide();
29523
29524     if(config.width){
29525         el.setWidth(config.width);
29526     }
29527     if(config.height){
29528         el.setHeight(config.height);
29529     }
29530     this.size = el.getSize();
29531     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
29532         this.xy = [config.x,config.y];
29533     }else{
29534         this.xy = el.getCenterXY(true);
29535     }
29536     /** The header element @type Roo.Element */
29537     this.header = el.child("> .x-dlg-hd");
29538     /** The body element @type Roo.Element */
29539     this.body = el.child("> .x-dlg-bd");
29540     /** The footer element @type Roo.Element */
29541     this.footer = el.child("> .x-dlg-ft");
29542
29543     if(!this.header){
29544         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
29545     }
29546     if(!this.body){
29547         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
29548     }
29549
29550     this.header.unselectable();
29551     if(this.title){
29552         this.header.update(this.title);
29553     }
29554     // this element allows the dialog to be focused for keyboard event
29555     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
29556     this.focusEl.swallowEvent("click", true);
29557
29558     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
29559
29560     // wrap the body and footer for special rendering
29561     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
29562     if(this.footer){
29563         this.bwrap.dom.appendChild(this.footer.dom);
29564     }
29565
29566     this.bg = this.el.createChild({
29567         tag: "div", cls:"x-dlg-bg",
29568         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
29569     });
29570     this.centerBg = this.bg.child("div.x-dlg-bg-center");
29571
29572
29573     if(this.autoScroll !== false && !this.autoTabs){
29574         this.body.setStyle("overflow", "auto");
29575     }
29576
29577     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
29578
29579     if(this.closable !== false){
29580         this.el.addClass("x-dlg-closable");
29581         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
29582         this.close.on("click", this.closeClick, this);
29583         this.close.addClassOnOver("x-dlg-close-over");
29584     }
29585     if(this.collapsible !== false){
29586         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
29587         this.collapseBtn.on("click", this.collapseClick, this);
29588         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
29589         this.header.on("dblclick", this.collapseClick, this);
29590     }
29591     if(this.resizable !== false){
29592         this.el.addClass("x-dlg-resizable");
29593         this.resizer = new Roo.Resizable(el, {
29594             minWidth: this.minWidth || 80,
29595             minHeight:this.minHeight || 80,
29596             handles: this.resizeHandles || "all",
29597             pinned: true
29598         });
29599         this.resizer.on("beforeresize", this.beforeResize, this);
29600         this.resizer.on("resize", this.onResize, this);
29601     }
29602     if(this.draggable !== false){
29603         el.addClass("x-dlg-draggable");
29604         if (!this.proxyDrag) {
29605             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
29606         }
29607         else {
29608             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
29609         }
29610         dd.setHandleElId(this.header.id);
29611         dd.endDrag = this.endMove.createDelegate(this);
29612         dd.startDrag = this.startMove.createDelegate(this);
29613         dd.onDrag = this.onDrag.createDelegate(this);
29614         dd.scroll = false;
29615         this.dd = dd;
29616     }
29617     if(this.modal){
29618         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
29619         this.mask.enableDisplayMode("block");
29620         this.mask.hide();
29621         this.el.addClass("x-dlg-modal");
29622     }
29623     if(this.shadow){
29624         this.shadow = new Roo.Shadow({
29625             mode : typeof this.shadow == "string" ? this.shadow : "sides",
29626             offset : this.shadowOffset
29627         });
29628     }else{
29629         this.shadowOffset = 0;
29630     }
29631     if(Roo.useShims && this.shim !== false){
29632         this.shim = this.el.createShim();
29633         this.shim.hide = this.hideAction;
29634         this.shim.hide();
29635     }else{
29636         this.shim = false;
29637     }
29638     if(this.autoTabs){
29639         this.initTabs();
29640     }
29641     if (this.buttons) { 
29642         var bts= this.buttons;
29643         this.buttons = [];
29644         Roo.each(bts, function(b) {
29645             this.addButton(b);
29646         }, this);
29647     }
29648     
29649     
29650     this.addEvents({
29651         /**
29652          * @event keydown
29653          * Fires when a key is pressed
29654          * @param {Roo.BasicDialog} this
29655          * @param {Roo.EventObject} e
29656          */
29657         "keydown" : true,
29658         /**
29659          * @event move
29660          * Fires when this dialog is moved by the user.
29661          * @param {Roo.BasicDialog} this
29662          * @param {Number} x The new page X
29663          * @param {Number} y The new page Y
29664          */
29665         "move" : true,
29666         /**
29667          * @event resize
29668          * Fires when this dialog is resized by the user.
29669          * @param {Roo.BasicDialog} this
29670          * @param {Number} width The new width
29671          * @param {Number} height The new height
29672          */
29673         "resize" : true,
29674         /**
29675          * @event beforehide
29676          * Fires before this dialog is hidden.
29677          * @param {Roo.BasicDialog} this
29678          */
29679         "beforehide" : true,
29680         /**
29681          * @event hide
29682          * Fires when this dialog is hidden.
29683          * @param {Roo.BasicDialog} this
29684          */
29685         "hide" : true,
29686         /**
29687          * @event beforeshow
29688          * Fires before this dialog is shown.
29689          * @param {Roo.BasicDialog} this
29690          */
29691         "beforeshow" : true,
29692         /**
29693          * @event show
29694          * Fires when this dialog is shown.
29695          * @param {Roo.BasicDialog} this
29696          */
29697         "show" : true
29698     });
29699     el.on("keydown", this.onKeyDown, this);
29700     el.on("mousedown", this.toFront, this);
29701     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
29702     this.el.hide();
29703     Roo.DialogManager.register(this);
29704     Roo.BasicDialog.superclass.constructor.call(this);
29705 };
29706
29707 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29708     shadowOffset: Roo.isIE ? 6 : 5,
29709     minHeight: 80,
29710     minWidth: 200,
29711     minButtonWidth: 75,
29712     defaultButton: null,
29713     buttonAlign: "right",
29714     tabTag: 'div',
29715     firstShow: true,
29716
29717     /**
29718      * Sets the dialog title text
29719      * @param {String} text The title text to display
29720      * @return {Roo.BasicDialog} this
29721      */
29722     setTitle : function(text){
29723         this.header.update(text);
29724         return this;
29725     },
29726
29727     // private
29728     closeClick : function(){
29729         this.hide();
29730     },
29731
29732     // private
29733     collapseClick : function(){
29734         this[this.collapsed ? "expand" : "collapse"]();
29735     },
29736
29737     /**
29738      * Collapses the dialog to its minimized state (only the title bar is visible).
29739      * Equivalent to the user clicking the collapse dialog button.
29740      */
29741     collapse : function(){
29742         if(!this.collapsed){
29743             this.collapsed = true;
29744             this.el.addClass("x-dlg-collapsed");
29745             this.restoreHeight = this.el.getHeight();
29746             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29747         }
29748     },
29749
29750     /**
29751      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29752      * clicking the expand dialog button.
29753      */
29754     expand : function(){
29755         if(this.collapsed){
29756             this.collapsed = false;
29757             this.el.removeClass("x-dlg-collapsed");
29758             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29759         }
29760     },
29761
29762     /**
29763      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29764      * @return {Roo.TabPanel} The tabs component
29765      */
29766     initTabs : function(){
29767         var tabs = this.getTabs();
29768         while(tabs.getTab(0)){
29769             tabs.removeTab(0);
29770         }
29771         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29772             var dom = el.dom;
29773             tabs.addTab(Roo.id(dom), dom.title);
29774             dom.title = "";
29775         });
29776         tabs.activate(0);
29777         return tabs;
29778     },
29779
29780     // private
29781     beforeResize : function(){
29782         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29783     },
29784
29785     // private
29786     onResize : function(){
29787         this.refreshSize();
29788         this.syncBodyHeight();
29789         this.adjustAssets();
29790         this.focus();
29791         this.fireEvent("resize", this, this.size.width, this.size.height);
29792     },
29793
29794     // private
29795     onKeyDown : function(e){
29796         if(this.isVisible()){
29797             this.fireEvent("keydown", this, e);
29798         }
29799     },
29800
29801     /**
29802      * Resizes the dialog.
29803      * @param {Number} width
29804      * @param {Number} height
29805      * @return {Roo.BasicDialog} this
29806      */
29807     resizeTo : function(width, height){
29808         this.el.setSize(width, height);
29809         this.size = {width: width, height: height};
29810         this.syncBodyHeight();
29811         if(this.fixedcenter){
29812             this.center();
29813         }
29814         if(this.isVisible()){
29815             this.constrainXY();
29816             this.adjustAssets();
29817         }
29818         this.fireEvent("resize", this, width, height);
29819         return this;
29820     },
29821
29822
29823     /**
29824      * Resizes the dialog to fit the specified content size.
29825      * @param {Number} width
29826      * @param {Number} height
29827      * @return {Roo.BasicDialog} this
29828      */
29829     setContentSize : function(w, h){
29830         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29831         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29832         //if(!this.el.isBorderBox()){
29833             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29834             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29835         //}
29836         if(this.tabs){
29837             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29838             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29839         }
29840         this.resizeTo(w, h);
29841         return this;
29842     },
29843
29844     /**
29845      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29846      * executed in response to a particular key being pressed while the dialog is active.
29847      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29848      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29849      * @param {Function} fn The function to call
29850      * @param {Object} scope (optional) The scope of the function
29851      * @return {Roo.BasicDialog} this
29852      */
29853     addKeyListener : function(key, fn, scope){
29854         var keyCode, shift, ctrl, alt;
29855         if(typeof key == "object" && !(key instanceof Array)){
29856             keyCode = key["key"];
29857             shift = key["shift"];
29858             ctrl = key["ctrl"];
29859             alt = key["alt"];
29860         }else{
29861             keyCode = key;
29862         }
29863         var handler = function(dlg, e){
29864             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29865                 var k = e.getKey();
29866                 if(keyCode instanceof Array){
29867                     for(var i = 0, len = keyCode.length; i < len; i++){
29868                         if(keyCode[i] == k){
29869                           fn.call(scope || window, dlg, k, e);
29870                           return;
29871                         }
29872                     }
29873                 }else{
29874                     if(k == keyCode){
29875                         fn.call(scope || window, dlg, k, e);
29876                     }
29877                 }
29878             }
29879         };
29880         this.on("keydown", handler);
29881         return this;
29882     },
29883
29884     /**
29885      * Returns the TabPanel component (creates it if it doesn't exist).
29886      * Note: If you wish to simply check for the existence of tabs without creating them,
29887      * check for a null 'tabs' property.
29888      * @return {Roo.TabPanel} The tabs component
29889      */
29890     getTabs : function(){
29891         if(!this.tabs){
29892             this.el.addClass("x-dlg-auto-tabs");
29893             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29894             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29895         }
29896         return this.tabs;
29897     },
29898
29899     /**
29900      * Adds a button to the footer section of the dialog.
29901      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29902      * object or a valid Roo.DomHelper element config
29903      * @param {Function} handler The function called when the button is clicked
29904      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29905      * @return {Roo.Button} The new button
29906      */
29907     addButton : function(config, handler, scope){
29908         var dh = Roo.DomHelper;
29909         if(!this.footer){
29910             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29911         }
29912         if(!this.btnContainer){
29913             var tb = this.footer.createChild({
29914
29915                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29916                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29917             }, null, true);
29918             this.btnContainer = tb.firstChild.firstChild.firstChild;
29919         }
29920         var bconfig = {
29921             handler: handler,
29922             scope: scope,
29923             minWidth: this.minButtonWidth,
29924             hideParent:true
29925         };
29926         if(typeof config == "string"){
29927             bconfig.text = config;
29928         }else{
29929             if(config.tag){
29930                 bconfig.dhconfig = config;
29931             }else{
29932                 Roo.apply(bconfig, config);
29933             }
29934         }
29935         var fc = false;
29936         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29937             bconfig.position = Math.max(0, bconfig.position);
29938             fc = this.btnContainer.childNodes[bconfig.position];
29939         }
29940          
29941         var btn = new Roo.Button(
29942             fc ? 
29943                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29944                 : this.btnContainer.appendChild(document.createElement("td")),
29945             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29946             bconfig
29947         );
29948         this.syncBodyHeight();
29949         if(!this.buttons){
29950             /**
29951              * Array of all the buttons that have been added to this dialog via addButton
29952              * @type Array
29953              */
29954             this.buttons = [];
29955         }
29956         this.buttons.push(btn);
29957         return btn;
29958     },
29959
29960     /**
29961      * Sets the default button to be focused when the dialog is displayed.
29962      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29963      * @return {Roo.BasicDialog} this
29964      */
29965     setDefaultButton : function(btn){
29966         this.defaultButton = btn;
29967         return this;
29968     },
29969
29970     // private
29971     getHeaderFooterHeight : function(safe){
29972         var height = 0;
29973         if(this.header){
29974            height += this.header.getHeight();
29975         }
29976         if(this.footer){
29977            var fm = this.footer.getMargins();
29978             height += (this.footer.getHeight()+fm.top+fm.bottom);
29979         }
29980         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29981         height += this.centerBg.getPadding("tb");
29982         return height;
29983     },
29984
29985     // private
29986     syncBodyHeight : function()
29987     {
29988         var bd = this.body, // the text
29989             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
29990             bw = this.bwrap;
29991         var height = this.size.height - this.getHeaderFooterHeight(false);
29992         bd.setHeight(height-bd.getMargins("tb"));
29993         var hh = this.header.getHeight();
29994         var h = this.size.height-hh;
29995         cb.setHeight(h);
29996         
29997         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29998         bw.setHeight(h-cb.getPadding("tb"));
29999         
30000         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30001         bd.setWidth(bw.getWidth(true));
30002         if(this.tabs){
30003             this.tabs.syncHeight();
30004             if(Roo.isIE){
30005                 this.tabs.el.repaint();
30006             }
30007         }
30008     },
30009
30010     /**
30011      * Restores the previous state of the dialog if Roo.state is configured.
30012      * @return {Roo.BasicDialog} this
30013      */
30014     restoreState : function(){
30015         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30016         if(box && box.width){
30017             this.xy = [box.x, box.y];
30018             this.resizeTo(box.width, box.height);
30019         }
30020         return this;
30021     },
30022
30023     // private
30024     beforeShow : function(){
30025         this.expand();
30026         if(this.fixedcenter){
30027             this.xy = this.el.getCenterXY(true);
30028         }
30029         if(this.modal){
30030             Roo.get(document.body).addClass("x-body-masked");
30031             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30032             this.mask.show();
30033         }
30034         this.constrainXY();
30035     },
30036
30037     // private
30038     animShow : function(){
30039         var b = Roo.get(this.animateTarget).getBox();
30040         this.proxy.setSize(b.width, b.height);
30041         this.proxy.setLocation(b.x, b.y);
30042         this.proxy.show();
30043         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30044                     true, .35, this.showEl.createDelegate(this));
30045     },
30046
30047     /**
30048      * Shows the dialog.
30049      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30050      * @return {Roo.BasicDialog} this
30051      */
30052     show : function(animateTarget){
30053         if (this.fireEvent("beforeshow", this) === false){
30054             return;
30055         }
30056         if(this.syncHeightBeforeShow){
30057             this.syncBodyHeight();
30058         }else if(this.firstShow){
30059             this.firstShow = false;
30060             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30061         }
30062         this.animateTarget = animateTarget || this.animateTarget;
30063         if(!this.el.isVisible()){
30064             this.beforeShow();
30065             if(this.animateTarget && Roo.get(this.animateTarget)){
30066                 this.animShow();
30067             }else{
30068                 this.showEl();
30069             }
30070         }
30071         return this;
30072     },
30073
30074     // private
30075     showEl : function(){
30076         this.proxy.hide();
30077         this.el.setXY(this.xy);
30078         this.el.show();
30079         this.adjustAssets(true);
30080         this.toFront();
30081         this.focus();
30082         // IE peekaboo bug - fix found by Dave Fenwick
30083         if(Roo.isIE){
30084             this.el.repaint();
30085         }
30086         this.fireEvent("show", this);
30087     },
30088
30089     /**
30090      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30091      * dialog itself will receive focus.
30092      */
30093     focus : function(){
30094         if(this.defaultButton){
30095             this.defaultButton.focus();
30096         }else{
30097             this.focusEl.focus();
30098         }
30099     },
30100
30101     // private
30102     constrainXY : function(){
30103         if(this.constraintoviewport !== false){
30104             if(!this.viewSize){
30105                 if(this.container){
30106                     var s = this.container.getSize();
30107                     this.viewSize = [s.width, s.height];
30108                 }else{
30109                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30110                 }
30111             }
30112             var s = Roo.get(this.container||document).getScroll();
30113
30114             var x = this.xy[0], y = this.xy[1];
30115             var w = this.size.width, h = this.size.height;
30116             var vw = this.viewSize[0], vh = this.viewSize[1];
30117             // only move it if it needs it
30118             var moved = false;
30119             // first validate right/bottom
30120             if(x + w > vw+s.left){
30121                 x = vw - w;
30122                 moved = true;
30123             }
30124             if(y + h > vh+s.top){
30125                 y = vh - h;
30126                 moved = true;
30127             }
30128             // then make sure top/left isn't negative
30129             if(x < s.left){
30130                 x = s.left;
30131                 moved = true;
30132             }
30133             if(y < s.top){
30134                 y = s.top;
30135                 moved = true;
30136             }
30137             if(moved){
30138                 // cache xy
30139                 this.xy = [x, y];
30140                 if(this.isVisible()){
30141                     this.el.setLocation(x, y);
30142                     this.adjustAssets();
30143                 }
30144             }
30145         }
30146     },
30147
30148     // private
30149     onDrag : function(){
30150         if(!this.proxyDrag){
30151             this.xy = this.el.getXY();
30152             this.adjustAssets();
30153         }
30154     },
30155
30156     // private
30157     adjustAssets : function(doShow){
30158         var x = this.xy[0], y = this.xy[1];
30159         var w = this.size.width, h = this.size.height;
30160         if(doShow === true){
30161             if(this.shadow){
30162                 this.shadow.show(this.el);
30163             }
30164             if(this.shim){
30165                 this.shim.show();
30166             }
30167         }
30168         if(this.shadow && this.shadow.isVisible()){
30169             this.shadow.show(this.el);
30170         }
30171         if(this.shim && this.shim.isVisible()){
30172             this.shim.setBounds(x, y, w, h);
30173         }
30174     },
30175
30176     // private
30177     adjustViewport : function(w, h){
30178         if(!w || !h){
30179             w = Roo.lib.Dom.getViewWidth();
30180             h = Roo.lib.Dom.getViewHeight();
30181         }
30182         // cache the size
30183         this.viewSize = [w, h];
30184         if(this.modal && this.mask.isVisible()){
30185             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30186             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30187         }
30188         if(this.isVisible()){
30189             this.constrainXY();
30190         }
30191     },
30192
30193     /**
30194      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30195      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30196      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30197      */
30198     destroy : function(removeEl){
30199         if(this.isVisible()){
30200             this.animateTarget = null;
30201             this.hide();
30202         }
30203         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30204         if(this.tabs){
30205             this.tabs.destroy(removeEl);
30206         }
30207         Roo.destroy(
30208              this.shim,
30209              this.proxy,
30210              this.resizer,
30211              this.close,
30212              this.mask
30213         );
30214         if(this.dd){
30215             this.dd.unreg();
30216         }
30217         if(this.buttons){
30218            for(var i = 0, len = this.buttons.length; i < len; i++){
30219                this.buttons[i].destroy();
30220            }
30221         }
30222         this.el.removeAllListeners();
30223         if(removeEl === true){
30224             this.el.update("");
30225             this.el.remove();
30226         }
30227         Roo.DialogManager.unregister(this);
30228     },
30229
30230     // private
30231     startMove : function(){
30232         if(this.proxyDrag){
30233             this.proxy.show();
30234         }
30235         if(this.constraintoviewport !== false){
30236             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30237         }
30238     },
30239
30240     // private
30241     endMove : function(){
30242         if(!this.proxyDrag){
30243             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30244         }else{
30245             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30246             this.proxy.hide();
30247         }
30248         this.refreshSize();
30249         this.adjustAssets();
30250         this.focus();
30251         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30252     },
30253
30254     /**
30255      * Brings this dialog to the front of any other visible dialogs
30256      * @return {Roo.BasicDialog} this
30257      */
30258     toFront : function(){
30259         Roo.DialogManager.bringToFront(this);
30260         return this;
30261     },
30262
30263     /**
30264      * Sends this dialog to the back (under) of any other visible dialogs
30265      * @return {Roo.BasicDialog} this
30266      */
30267     toBack : function(){
30268         Roo.DialogManager.sendToBack(this);
30269         return this;
30270     },
30271
30272     /**
30273      * Centers this dialog in the viewport
30274      * @return {Roo.BasicDialog} this
30275      */
30276     center : function(){
30277         var xy = this.el.getCenterXY(true);
30278         this.moveTo(xy[0], xy[1]);
30279         return this;
30280     },
30281
30282     /**
30283      * Moves the dialog's top-left corner to the specified point
30284      * @param {Number} x
30285      * @param {Number} y
30286      * @return {Roo.BasicDialog} this
30287      */
30288     moveTo : function(x, y){
30289         this.xy = [x,y];
30290         if(this.isVisible()){
30291             this.el.setXY(this.xy);
30292             this.adjustAssets();
30293         }
30294         return this;
30295     },
30296
30297     /**
30298      * Aligns the dialog to the specified element
30299      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30300      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30301      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30302      * @return {Roo.BasicDialog} this
30303      */
30304     alignTo : function(element, position, offsets){
30305         this.xy = this.el.getAlignToXY(element, position, offsets);
30306         if(this.isVisible()){
30307             this.el.setXY(this.xy);
30308             this.adjustAssets();
30309         }
30310         return this;
30311     },
30312
30313     /**
30314      * Anchors an element to another element and realigns it when the window is resized.
30315      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30316      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30317      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30318      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30319      * is a number, it is used as the buffer delay (defaults to 50ms).
30320      * @return {Roo.BasicDialog} this
30321      */
30322     anchorTo : function(el, alignment, offsets, monitorScroll){
30323         var action = function(){
30324             this.alignTo(el, alignment, offsets);
30325         };
30326         Roo.EventManager.onWindowResize(action, this);
30327         var tm = typeof monitorScroll;
30328         if(tm != 'undefined'){
30329             Roo.EventManager.on(window, 'scroll', action, this,
30330                 {buffer: tm == 'number' ? monitorScroll : 50});
30331         }
30332         action.call(this);
30333         return this;
30334     },
30335
30336     /**
30337      * Returns true if the dialog is visible
30338      * @return {Boolean}
30339      */
30340     isVisible : function(){
30341         return this.el.isVisible();
30342     },
30343
30344     // private
30345     animHide : function(callback){
30346         var b = Roo.get(this.animateTarget).getBox();
30347         this.proxy.show();
30348         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30349         this.el.hide();
30350         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30351                     this.hideEl.createDelegate(this, [callback]));
30352     },
30353
30354     /**
30355      * Hides the dialog.
30356      * @param {Function} callback (optional) Function to call when the dialog is hidden
30357      * @return {Roo.BasicDialog} this
30358      */
30359     hide : function(callback){
30360         if (this.fireEvent("beforehide", this) === false){
30361             return;
30362         }
30363         if(this.shadow){
30364             this.shadow.hide();
30365         }
30366         if(this.shim) {
30367           this.shim.hide();
30368         }
30369         // sometimes animateTarget seems to get set.. causing problems...
30370         // this just double checks..
30371         if(this.animateTarget && Roo.get(this.animateTarget)) {
30372            this.animHide(callback);
30373         }else{
30374             this.el.hide();
30375             this.hideEl(callback);
30376         }
30377         return this;
30378     },
30379
30380     // private
30381     hideEl : function(callback){
30382         this.proxy.hide();
30383         if(this.modal){
30384             this.mask.hide();
30385             Roo.get(document.body).removeClass("x-body-masked");
30386         }
30387         this.fireEvent("hide", this);
30388         if(typeof callback == "function"){
30389             callback();
30390         }
30391     },
30392
30393     // private
30394     hideAction : function(){
30395         this.setLeft("-10000px");
30396         this.setTop("-10000px");
30397         this.setStyle("visibility", "hidden");
30398     },
30399
30400     // private
30401     refreshSize : function(){
30402         this.size = this.el.getSize();
30403         this.xy = this.el.getXY();
30404         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30405     },
30406
30407     // private
30408     // z-index is managed by the DialogManager and may be overwritten at any time
30409     setZIndex : function(index){
30410         if(this.modal){
30411             this.mask.setStyle("z-index", index);
30412         }
30413         if(this.shim){
30414             this.shim.setStyle("z-index", ++index);
30415         }
30416         if(this.shadow){
30417             this.shadow.setZIndex(++index);
30418         }
30419         this.el.setStyle("z-index", ++index);
30420         if(this.proxy){
30421             this.proxy.setStyle("z-index", ++index);
30422         }
30423         if(this.resizer){
30424             this.resizer.proxy.setStyle("z-index", ++index);
30425         }
30426
30427         this.lastZIndex = index;
30428     },
30429
30430     /**
30431      * Returns the element for this dialog
30432      * @return {Roo.Element} The underlying dialog Element
30433      */
30434     getEl : function(){
30435         return this.el;
30436     }
30437 });
30438
30439 /**
30440  * @class Roo.DialogManager
30441  * Provides global access to BasicDialogs that have been created and
30442  * support for z-indexing (layering) multiple open dialogs.
30443  */
30444 Roo.DialogManager = function(){
30445     var list = {};
30446     var accessList = [];
30447     var front = null;
30448
30449     // private
30450     var sortDialogs = function(d1, d2){
30451         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30452     };
30453
30454     // private
30455     var orderDialogs = function(){
30456         accessList.sort(sortDialogs);
30457         var seed = Roo.DialogManager.zseed;
30458         for(var i = 0, len = accessList.length; i < len; i++){
30459             var dlg = accessList[i];
30460             if(dlg){
30461                 dlg.setZIndex(seed + (i*10));
30462             }
30463         }
30464     };
30465
30466     return {
30467         /**
30468          * The starting z-index for BasicDialogs (defaults to 9000)
30469          * @type Number The z-index value
30470          */
30471         zseed : 9000,
30472
30473         // private
30474         register : function(dlg){
30475             list[dlg.id] = dlg;
30476             accessList.push(dlg);
30477         },
30478
30479         // private
30480         unregister : function(dlg){
30481             delete list[dlg.id];
30482             var i=0;
30483             var len=0;
30484             if(!accessList.indexOf){
30485                 for(  i = 0, len = accessList.length; i < len; i++){
30486                     if(accessList[i] == dlg){
30487                         accessList.splice(i, 1);
30488                         return;
30489                     }
30490                 }
30491             }else{
30492                  i = accessList.indexOf(dlg);
30493                 if(i != -1){
30494                     accessList.splice(i, 1);
30495                 }
30496             }
30497         },
30498
30499         /**
30500          * Gets a registered dialog by id
30501          * @param {String/Object} id The id of the dialog or a dialog
30502          * @return {Roo.BasicDialog} this
30503          */
30504         get : function(id){
30505             return typeof id == "object" ? id : list[id];
30506         },
30507
30508         /**
30509          * Brings the specified dialog to the front
30510          * @param {String/Object} dlg The id of the dialog or a dialog
30511          * @return {Roo.BasicDialog} this
30512          */
30513         bringToFront : function(dlg){
30514             dlg = this.get(dlg);
30515             if(dlg != front){
30516                 front = dlg;
30517                 dlg._lastAccess = new Date().getTime();
30518                 orderDialogs();
30519             }
30520             return dlg;
30521         },
30522
30523         /**
30524          * Sends the specified dialog to the back
30525          * @param {String/Object} dlg The id of the dialog or a dialog
30526          * @return {Roo.BasicDialog} this
30527          */
30528         sendToBack : function(dlg){
30529             dlg = this.get(dlg);
30530             dlg._lastAccess = -(new Date().getTime());
30531             orderDialogs();
30532             return dlg;
30533         },
30534
30535         /**
30536          * Hides all dialogs
30537          */
30538         hideAll : function(){
30539             for(var id in list){
30540                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
30541                     list[id].hide();
30542                 }
30543             }
30544         }
30545     };
30546 }();
30547
30548 /**
30549  * @class Roo.LayoutDialog
30550  * @extends Roo.BasicDialog
30551  * Dialog which provides adjustments for working with a layout in a Dialog.
30552  * Add your necessary layout config options to the dialog's config.<br>
30553  * Example usage (including a nested layout):
30554  * <pre><code>
30555 if(!dialog){
30556     dialog = new Roo.LayoutDialog("download-dlg", {
30557         modal: true,
30558         width:600,
30559         height:450,
30560         shadow:true,
30561         minWidth:500,
30562         minHeight:350,
30563         autoTabs:true,
30564         proxyDrag:true,
30565         // layout config merges with the dialog config
30566         center:{
30567             tabPosition: "top",
30568             alwaysShowTabs: true
30569         }
30570     });
30571     dialog.addKeyListener(27, dialog.hide, dialog);
30572     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
30573     dialog.addButton("Build It!", this.getDownload, this);
30574
30575     // we can even add nested layouts
30576     var innerLayout = new Roo.BorderLayout("dl-inner", {
30577         east: {
30578             initialSize: 200,
30579             autoScroll:true,
30580             split:true
30581         },
30582         center: {
30583             autoScroll:true
30584         }
30585     });
30586     innerLayout.beginUpdate();
30587     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
30588     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
30589     innerLayout.endUpdate(true);
30590
30591     var layout = dialog.getLayout();
30592     layout.beginUpdate();
30593     layout.add("center", new Roo.ContentPanel("standard-panel",
30594                         {title: "Download the Source", fitToFrame:true}));
30595     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
30596                {title: "Build your own roo.js"}));
30597     layout.getRegion("center").showPanel(sp);
30598     layout.endUpdate();
30599 }
30600 </code></pre>
30601     * @constructor
30602     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
30603     * @param {Object} config configuration options
30604   */
30605 Roo.LayoutDialog = function(el, cfg){
30606     
30607     var config=  cfg;
30608     if (typeof(cfg) == 'undefined') {
30609         config = Roo.apply({}, el);
30610         // not sure why we use documentElement here.. - it should always be body.
30611         // IE7 borks horribly if we use documentElement.
30612         // webkit also does not like documentElement - it creates a body element...
30613         el = Roo.get( document.body || document.documentElement ).createChild();
30614         //config.autoCreate = true;
30615     }
30616     
30617     
30618     config.autoTabs = false;
30619     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
30620     this.body.setStyle({overflow:"hidden", position:"relative"});
30621     this.layout = new Roo.BorderLayout(this.body.dom, config);
30622     this.layout.monitorWindowResize = false;
30623     this.el.addClass("x-dlg-auto-layout");
30624     // fix case when center region overwrites center function
30625     this.center = Roo.BasicDialog.prototype.center;
30626     this.on("show", this.layout.layout, this.layout, true);
30627     if (config.items) {
30628         var xitems = config.items;
30629         delete config.items;
30630         Roo.each(xitems, this.addxtype, this);
30631     }
30632     
30633     
30634 };
30635 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
30636     /**
30637      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
30638      * @deprecated
30639      */
30640     endUpdate : function(){
30641         this.layout.endUpdate();
30642     },
30643
30644     /**
30645      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
30646      *  @deprecated
30647      */
30648     beginUpdate : function(){
30649         this.layout.beginUpdate();
30650     },
30651
30652     /**
30653      * Get the BorderLayout for this dialog
30654      * @return {Roo.BorderLayout}
30655      */
30656     getLayout : function(){
30657         return this.layout;
30658     },
30659
30660     showEl : function(){
30661         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
30662         if(Roo.isIE7){
30663             this.layout.layout();
30664         }
30665     },
30666
30667     // private
30668     // Use the syncHeightBeforeShow config option to control this automatically
30669     syncBodyHeight : function(){
30670         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
30671         if(this.layout){this.layout.layout();}
30672     },
30673     
30674       /**
30675      * Add an xtype element (actually adds to the layout.)
30676      * @return {Object} xdata xtype object data.
30677      */
30678     
30679     addxtype : function(c) {
30680         return this.layout.addxtype(c);
30681     }
30682 });/*
30683  * Based on:
30684  * Ext JS Library 1.1.1
30685  * Copyright(c) 2006-2007, Ext JS, LLC.
30686  *
30687  * Originally Released Under LGPL - original licence link has changed is not relivant.
30688  *
30689  * Fork - LGPL
30690  * <script type="text/javascript">
30691  */
30692  
30693 /**
30694  * @class Roo.MessageBox
30695  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
30696  * Example usage:
30697  *<pre><code>
30698 // Basic alert:
30699 Roo.Msg.alert('Status', 'Changes saved successfully.');
30700
30701 // Prompt for user data:
30702 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
30703     if (btn == 'ok'){
30704         // process text value...
30705     }
30706 });
30707
30708 // Show a dialog using config options:
30709 Roo.Msg.show({
30710    title:'Save Changes?',
30711    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
30712    buttons: Roo.Msg.YESNOCANCEL,
30713    fn: processResult,
30714    animEl: 'elId'
30715 });
30716 </code></pre>
30717  * @singleton
30718  */
30719 Roo.MessageBox = function(){
30720     var dlg, opt, mask, waitTimer;
30721     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30722     var buttons, activeTextEl, bwidth;
30723
30724     // private
30725     var handleButton = function(button){
30726         dlg.hide();
30727         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30728     };
30729
30730     // private
30731     var handleHide = function(){
30732         if(opt && opt.cls){
30733             dlg.el.removeClass(opt.cls);
30734         }
30735         if(waitTimer){
30736             Roo.TaskMgr.stop(waitTimer);
30737             waitTimer = null;
30738         }
30739     };
30740
30741     // private
30742     var updateButtons = function(b){
30743         var width = 0;
30744         if(!b){
30745             buttons["ok"].hide();
30746             buttons["cancel"].hide();
30747             buttons["yes"].hide();
30748             buttons["no"].hide();
30749             dlg.footer.dom.style.display = 'none';
30750             return width;
30751         }
30752         dlg.footer.dom.style.display = '';
30753         for(var k in buttons){
30754             if(typeof buttons[k] != "function"){
30755                 if(b[k]){
30756                     buttons[k].show();
30757                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30758                     width += buttons[k].el.getWidth()+15;
30759                 }else{
30760                     buttons[k].hide();
30761                 }
30762             }
30763         }
30764         return width;
30765     };
30766
30767     // private
30768     var handleEsc = function(d, k, e){
30769         if(opt && opt.closable !== false){
30770             dlg.hide();
30771         }
30772         if(e){
30773             e.stopEvent();
30774         }
30775     };
30776
30777     return {
30778         /**
30779          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30780          * @return {Roo.BasicDialog} The BasicDialog element
30781          */
30782         getDialog : function(){
30783            if(!dlg){
30784                 dlg = new Roo.BasicDialog("x-msg-box", {
30785                     autoCreate : true,
30786                     shadow: true,
30787                     draggable: true,
30788                     resizable:false,
30789                     constraintoviewport:false,
30790                     fixedcenter:true,
30791                     collapsible : false,
30792                     shim:true,
30793                     modal: true,
30794                     width:400, height:100,
30795                     buttonAlign:"center",
30796                     closeClick : function(){
30797                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30798                             handleButton("no");
30799                         }else{
30800                             handleButton("cancel");
30801                         }
30802                     }
30803                 });
30804                 dlg.on("hide", handleHide);
30805                 mask = dlg.mask;
30806                 dlg.addKeyListener(27, handleEsc);
30807                 buttons = {};
30808                 var bt = this.buttonText;
30809                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30810                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30811                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30812                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30813                 bodyEl = dlg.body.createChild({
30814
30815                     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>'
30816                 });
30817                 msgEl = bodyEl.dom.firstChild;
30818                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30819                 textboxEl.enableDisplayMode();
30820                 textboxEl.addKeyListener([10,13], function(){
30821                     if(dlg.isVisible() && opt && opt.buttons){
30822                         if(opt.buttons.ok){
30823                             handleButton("ok");
30824                         }else if(opt.buttons.yes){
30825                             handleButton("yes");
30826                         }
30827                     }
30828                 });
30829                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30830                 textareaEl.enableDisplayMode();
30831                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30832                 progressEl.enableDisplayMode();
30833                 var pf = progressEl.dom.firstChild;
30834                 if (pf) {
30835                     pp = Roo.get(pf.firstChild);
30836                     pp.setHeight(pf.offsetHeight);
30837                 }
30838                 
30839             }
30840             return dlg;
30841         },
30842
30843         /**
30844          * Updates the message box body text
30845          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30846          * the XHTML-compliant non-breaking space character '&amp;#160;')
30847          * @return {Roo.MessageBox} This message box
30848          */
30849         updateText : function(text){
30850             if(!dlg.isVisible() && !opt.width){
30851                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30852             }
30853             msgEl.innerHTML = text || '&#160;';
30854       
30855             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30856             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30857             var w = Math.max(
30858                     Math.min(opt.width || cw , this.maxWidth), 
30859                     Math.max(opt.minWidth || this.minWidth, bwidth)
30860             );
30861             if(opt.prompt){
30862                 activeTextEl.setWidth(w);
30863             }
30864             if(dlg.isVisible()){
30865                 dlg.fixedcenter = false;
30866             }
30867             // to big, make it scroll. = But as usual stupid IE does not support
30868             // !important..
30869             
30870             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30871                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30872                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30873             } else {
30874                 bodyEl.dom.style.height = '';
30875                 bodyEl.dom.style.overflowY = '';
30876             }
30877             if (cw > w) {
30878                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30879             } else {
30880                 bodyEl.dom.style.overflowX = '';
30881             }
30882             
30883             dlg.setContentSize(w, bodyEl.getHeight());
30884             if(dlg.isVisible()){
30885                 dlg.fixedcenter = true;
30886             }
30887             return this;
30888         },
30889
30890         /**
30891          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30892          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30893          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30894          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30895          * @return {Roo.MessageBox} This message box
30896          */
30897         updateProgress : function(value, text){
30898             if(text){
30899                 this.updateText(text);
30900             }
30901             if (pp) { // weird bug on my firefox - for some reason this is not defined
30902                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30903             }
30904             return this;
30905         },        
30906
30907         /**
30908          * Returns true if the message box is currently displayed
30909          * @return {Boolean} True if the message box is visible, else false
30910          */
30911         isVisible : function(){
30912             return dlg && dlg.isVisible();  
30913         },
30914
30915         /**
30916          * Hides the message box if it is displayed
30917          */
30918         hide : function(){
30919             if(this.isVisible()){
30920                 dlg.hide();
30921             }  
30922         },
30923
30924         /**
30925          * Displays a new message box, or reinitializes an existing message box, based on the config options
30926          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30927          * The following config object properties are supported:
30928          * <pre>
30929 Property    Type             Description
30930 ----------  ---------------  ------------------------------------------------------------------------------------
30931 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30932                                    closes (defaults to undefined)
30933 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30934                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30935 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30936                                    progress and wait dialogs will ignore this property and always hide the
30937                                    close button as they can only be closed programmatically.
30938 cls               String           A custom CSS class to apply to the message box element
30939 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30940                                    displayed (defaults to 75)
30941 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30942                                    function will be btn (the name of the button that was clicked, if applicable,
30943                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30944                                    Progress and wait dialogs will ignore this option since they do not respond to
30945                                    user actions and can only be closed programmatically, so any required function
30946                                    should be called by the same code after it closes the dialog.
30947 icon              String           A CSS class that provides a background image to be used as an icon for
30948                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30949 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30950 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30951 modal             Boolean          False to allow user interaction with the page while the message box is
30952                                    displayed (defaults to true)
30953 msg               String           A string that will replace the existing message box body text (defaults
30954                                    to the XHTML-compliant non-breaking space character '&#160;')
30955 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30956 progress          Boolean          True to display a progress bar (defaults to false)
30957 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30958 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30959 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30960 title             String           The title text
30961 value             String           The string value to set into the active textbox element if displayed
30962 wait              Boolean          True to display a progress bar (defaults to false)
30963 width             Number           The width of the dialog in pixels
30964 </pre>
30965          *
30966          * Example usage:
30967          * <pre><code>
30968 Roo.Msg.show({
30969    title: 'Address',
30970    msg: 'Please enter your address:',
30971    width: 300,
30972    buttons: Roo.MessageBox.OKCANCEL,
30973    multiline: true,
30974    fn: saveAddress,
30975    animEl: 'addAddressBtn'
30976 });
30977 </code></pre>
30978          * @param {Object} config Configuration options
30979          * @return {Roo.MessageBox} This message box
30980          */
30981         show : function(options)
30982         {
30983             
30984             // this causes nightmares if you show one dialog after another
30985             // especially on callbacks..
30986              
30987             if(this.isVisible()){
30988                 
30989                 this.hide();
30990                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
30991                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
30992                 Roo.log("New Dialog Message:" +  options.msg )
30993                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30994                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30995                 
30996             }
30997             var d = this.getDialog();
30998             opt = options;
30999             d.setTitle(opt.title || "&#160;");
31000             d.close.setDisplayed(opt.closable !== false);
31001             activeTextEl = textboxEl;
31002             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31003             if(opt.prompt){
31004                 if(opt.multiline){
31005                     textboxEl.hide();
31006                     textareaEl.show();
31007                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31008                         opt.multiline : this.defaultTextHeight);
31009                     activeTextEl = textareaEl;
31010                 }else{
31011                     textboxEl.show();
31012                     textareaEl.hide();
31013                 }
31014             }else{
31015                 textboxEl.hide();
31016                 textareaEl.hide();
31017             }
31018             progressEl.setDisplayed(opt.progress === true);
31019             this.updateProgress(0);
31020             activeTextEl.dom.value = opt.value || "";
31021             if(opt.prompt){
31022                 dlg.setDefaultButton(activeTextEl);
31023             }else{
31024                 var bs = opt.buttons;
31025                 var db = null;
31026                 if(bs && bs.ok){
31027                     db = buttons["ok"];
31028                 }else if(bs && bs.yes){
31029                     db = buttons["yes"];
31030                 }
31031                 dlg.setDefaultButton(db);
31032             }
31033             bwidth = updateButtons(opt.buttons);
31034             this.updateText(opt.msg);
31035             if(opt.cls){
31036                 d.el.addClass(opt.cls);
31037             }
31038             d.proxyDrag = opt.proxyDrag === true;
31039             d.modal = opt.modal !== false;
31040             d.mask = opt.modal !== false ? mask : false;
31041             if(!d.isVisible()){
31042                 // force it to the end of the z-index stack so it gets a cursor in FF
31043                 document.body.appendChild(dlg.el.dom);
31044                 d.animateTarget = null;
31045                 d.show(options.animEl);
31046             }
31047             return this;
31048         },
31049
31050         /**
31051          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31052          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31053          * and closing the message box when the process is complete.
31054          * @param {String} title The title bar text
31055          * @param {String} msg The message box body text
31056          * @return {Roo.MessageBox} This message box
31057          */
31058         progress : function(title, msg){
31059             this.show({
31060                 title : title,
31061                 msg : msg,
31062                 buttons: false,
31063                 progress:true,
31064                 closable:false,
31065                 minWidth: this.minProgressWidth,
31066                 modal : true
31067             });
31068             return this;
31069         },
31070
31071         /**
31072          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31073          * If a callback function is passed it will be called after the user clicks the button, and the
31074          * id of the button that was clicked will be passed as the only parameter to the callback
31075          * (could also be the top-right close button).
31076          * @param {String} title The title bar text
31077          * @param {String} msg The message box body text
31078          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31079          * @param {Object} scope (optional) The scope of the callback function
31080          * @return {Roo.MessageBox} This message box
31081          */
31082         alert : function(title, msg, fn, scope){
31083             this.show({
31084                 title : title,
31085                 msg : msg,
31086                 buttons: this.OK,
31087                 fn: fn,
31088                 scope : scope,
31089                 modal : true
31090             });
31091             return this;
31092         },
31093
31094         /**
31095          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31096          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31097          * You are responsible for closing the message box when the process is complete.
31098          * @param {String} msg The message box body text
31099          * @param {String} title (optional) The title bar text
31100          * @return {Roo.MessageBox} This message box
31101          */
31102         wait : function(msg, title){
31103             this.show({
31104                 title : title,
31105                 msg : msg,
31106                 buttons: false,
31107                 closable:false,
31108                 progress:true,
31109                 modal:true,
31110                 width:300,
31111                 wait:true
31112             });
31113             waitTimer = Roo.TaskMgr.start({
31114                 run: function(i){
31115                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31116                 },
31117                 interval: 1000
31118             });
31119             return this;
31120         },
31121
31122         /**
31123          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31124          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31125          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31126          * @param {String} title The title bar text
31127          * @param {String} msg The message box body text
31128          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31129          * @param {Object} scope (optional) The scope of the callback function
31130          * @return {Roo.MessageBox} This message box
31131          */
31132         confirm : function(title, msg, fn, scope){
31133             this.show({
31134                 title : title,
31135                 msg : msg,
31136                 buttons: this.YESNO,
31137                 fn: fn,
31138                 scope : scope,
31139                 modal : true
31140             });
31141             return this;
31142         },
31143
31144         /**
31145          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31146          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31147          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31148          * (could also be the top-right close button) and the text that was entered will be passed as the two
31149          * parameters to the callback.
31150          * @param {String} title The title bar text
31151          * @param {String} msg The message box body text
31152          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31153          * @param {Object} scope (optional) The scope of the callback function
31154          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31155          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31156          * @return {Roo.MessageBox} This message box
31157          */
31158         prompt : function(title, msg, fn, scope, multiline){
31159             this.show({
31160                 title : title,
31161                 msg : msg,
31162                 buttons: this.OKCANCEL,
31163                 fn: fn,
31164                 minWidth:250,
31165                 scope : scope,
31166                 prompt:true,
31167                 multiline: multiline,
31168                 modal : true
31169             });
31170             return this;
31171         },
31172
31173         /**
31174          * Button config that displays a single OK button
31175          * @type Object
31176          */
31177         OK : {ok:true},
31178         /**
31179          * Button config that displays Yes and No buttons
31180          * @type Object
31181          */
31182         YESNO : {yes:true, no:true},
31183         /**
31184          * Button config that displays OK and Cancel buttons
31185          * @type Object
31186          */
31187         OKCANCEL : {ok:true, cancel:true},
31188         /**
31189          * Button config that displays Yes, No and Cancel buttons
31190          * @type Object
31191          */
31192         YESNOCANCEL : {yes:true, no:true, cancel:true},
31193
31194         /**
31195          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31196          * @type Number
31197          */
31198         defaultTextHeight : 75,
31199         /**
31200          * The maximum width in pixels of the message box (defaults to 600)
31201          * @type Number
31202          */
31203         maxWidth : 600,
31204         /**
31205          * The minimum width in pixels of the message box (defaults to 100)
31206          * @type Number
31207          */
31208         minWidth : 100,
31209         /**
31210          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31211          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31212          * @type Number
31213          */
31214         minProgressWidth : 250,
31215         /**
31216          * An object containing the default button text strings that can be overriden for localized language support.
31217          * Supported properties are: ok, cancel, yes and no.
31218          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31219          * @type Object
31220          */
31221         buttonText : {
31222             ok : "OK",
31223             cancel : "Cancel",
31224             yes : "Yes",
31225             no : "No"
31226         }
31227     };
31228 }();
31229
31230 /**
31231  * Shorthand for {@link Roo.MessageBox}
31232  */
31233 Roo.Msg = Roo.MessageBox;/*
31234  * Based on:
31235  * Ext JS Library 1.1.1
31236  * Copyright(c) 2006-2007, Ext JS, LLC.
31237  *
31238  * Originally Released Under LGPL - original licence link has changed is not relivant.
31239  *
31240  * Fork - LGPL
31241  * <script type="text/javascript">
31242  */
31243 /**
31244  * @class Roo.QuickTips
31245  * Provides attractive and customizable tooltips for any element.
31246  * @singleton
31247  */
31248 Roo.QuickTips = function(){
31249     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31250     var ce, bd, xy, dd;
31251     var visible = false, disabled = true, inited = false;
31252     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31253     
31254     var onOver = function(e){
31255         if(disabled){
31256             return;
31257         }
31258         var t = e.getTarget();
31259         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31260             return;
31261         }
31262         if(ce && t == ce.el){
31263             clearTimeout(hideProc);
31264             return;
31265         }
31266         if(t && tagEls[t.id]){
31267             tagEls[t.id].el = t;
31268             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31269             return;
31270         }
31271         var ttp, et = Roo.fly(t);
31272         var ns = cfg.namespace;
31273         if(tm.interceptTitles && t.title){
31274             ttp = t.title;
31275             t.qtip = ttp;
31276             t.removeAttribute("title");
31277             e.preventDefault();
31278         }else{
31279             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31280         }
31281         if(ttp){
31282             showProc = show.defer(tm.showDelay, tm, [{
31283                 el: t, 
31284                 text: ttp, 
31285                 width: et.getAttributeNS(ns, cfg.width),
31286                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31287                 title: et.getAttributeNS(ns, cfg.title),
31288                     cls: et.getAttributeNS(ns, cfg.cls)
31289             }]);
31290         }
31291     };
31292     
31293     var onOut = function(e){
31294         clearTimeout(showProc);
31295         var t = e.getTarget();
31296         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31297             hideProc = setTimeout(hide, tm.hideDelay);
31298         }
31299     };
31300     
31301     var onMove = function(e){
31302         if(disabled){
31303             return;
31304         }
31305         xy = e.getXY();
31306         xy[1] += 18;
31307         if(tm.trackMouse && ce){
31308             el.setXY(xy);
31309         }
31310     };
31311     
31312     var onDown = function(e){
31313         clearTimeout(showProc);
31314         clearTimeout(hideProc);
31315         if(!e.within(el)){
31316             if(tm.hideOnClick){
31317                 hide();
31318                 tm.disable();
31319                 tm.enable.defer(100, tm);
31320             }
31321         }
31322     };
31323     
31324     var getPad = function(){
31325         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31326     };
31327
31328     var show = function(o){
31329         if(disabled){
31330             return;
31331         }
31332         clearTimeout(dismissProc);
31333         ce = o;
31334         if(removeCls){ // in case manually hidden
31335             el.removeClass(removeCls);
31336             removeCls = null;
31337         }
31338         if(ce.cls){
31339             el.addClass(ce.cls);
31340             removeCls = ce.cls;
31341         }
31342         if(ce.title){
31343             tipTitle.update(ce.title);
31344             tipTitle.show();
31345         }else{
31346             tipTitle.update('');
31347             tipTitle.hide();
31348         }
31349         el.dom.style.width  = tm.maxWidth+'px';
31350         //tipBody.dom.style.width = '';
31351         tipBodyText.update(o.text);
31352         var p = getPad(), w = ce.width;
31353         if(!w){
31354             var td = tipBodyText.dom;
31355             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31356             if(aw > tm.maxWidth){
31357                 w = tm.maxWidth;
31358             }else if(aw < tm.minWidth){
31359                 w = tm.minWidth;
31360             }else{
31361                 w = aw;
31362             }
31363         }
31364         //tipBody.setWidth(w);
31365         el.setWidth(parseInt(w, 10) + p);
31366         if(ce.autoHide === false){
31367             close.setDisplayed(true);
31368             if(dd){
31369                 dd.unlock();
31370             }
31371         }else{
31372             close.setDisplayed(false);
31373             if(dd){
31374                 dd.lock();
31375             }
31376         }
31377         if(xy){
31378             el.avoidY = xy[1]-18;
31379             el.setXY(xy);
31380         }
31381         if(tm.animate){
31382             el.setOpacity(.1);
31383             el.setStyle("visibility", "visible");
31384             el.fadeIn({callback: afterShow});
31385         }else{
31386             afterShow();
31387         }
31388     };
31389     
31390     var afterShow = function(){
31391         if(ce){
31392             el.show();
31393             esc.enable();
31394             if(tm.autoDismiss && ce.autoHide !== false){
31395                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31396             }
31397         }
31398     };
31399     
31400     var hide = function(noanim){
31401         clearTimeout(dismissProc);
31402         clearTimeout(hideProc);
31403         ce = null;
31404         if(el.isVisible()){
31405             esc.disable();
31406             if(noanim !== true && tm.animate){
31407                 el.fadeOut({callback: afterHide});
31408             }else{
31409                 afterHide();
31410             } 
31411         }
31412     };
31413     
31414     var afterHide = function(){
31415         el.hide();
31416         if(removeCls){
31417             el.removeClass(removeCls);
31418             removeCls = null;
31419         }
31420     };
31421     
31422     return {
31423         /**
31424         * @cfg {Number} minWidth
31425         * The minimum width of the quick tip (defaults to 40)
31426         */
31427        minWidth : 40,
31428         /**
31429         * @cfg {Number} maxWidth
31430         * The maximum width of the quick tip (defaults to 300)
31431         */
31432        maxWidth : 300,
31433         /**
31434         * @cfg {Boolean} interceptTitles
31435         * True to automatically use the element's DOM title value if available (defaults to false)
31436         */
31437        interceptTitles : false,
31438         /**
31439         * @cfg {Boolean} trackMouse
31440         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31441         */
31442        trackMouse : false,
31443         /**
31444         * @cfg {Boolean} hideOnClick
31445         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31446         */
31447        hideOnClick : true,
31448         /**
31449         * @cfg {Number} showDelay
31450         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31451         */
31452        showDelay : 500,
31453         /**
31454         * @cfg {Number} hideDelay
31455         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31456         */
31457        hideDelay : 200,
31458         /**
31459         * @cfg {Boolean} autoHide
31460         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31461         * Used in conjunction with hideDelay.
31462         */
31463        autoHide : true,
31464         /**
31465         * @cfg {Boolean}
31466         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31467         * (defaults to true).  Used in conjunction with autoDismissDelay.
31468         */
31469        autoDismiss : true,
31470         /**
31471         * @cfg {Number}
31472         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31473         */
31474        autoDismissDelay : 5000,
31475        /**
31476         * @cfg {Boolean} animate
31477         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31478         */
31479        animate : false,
31480
31481        /**
31482         * @cfg {String} title
31483         * Title text to display (defaults to '').  This can be any valid HTML markup.
31484         */
31485         title: '',
31486        /**
31487         * @cfg {String} text
31488         * Body text to display (defaults to '').  This can be any valid HTML markup.
31489         */
31490         text : '',
31491        /**
31492         * @cfg {String} cls
31493         * A CSS class to apply to the base quick tip element (defaults to '').
31494         */
31495         cls : '',
31496        /**
31497         * @cfg {Number} width
31498         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
31499         * minWidth or maxWidth.
31500         */
31501         width : null,
31502
31503     /**
31504      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
31505      * or display QuickTips in a page.
31506      */
31507        init : function(){
31508           tm = Roo.QuickTips;
31509           cfg = tm.tagConfig;
31510           if(!inited){
31511               if(!Roo.isReady){ // allow calling of init() before onReady
31512                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
31513                   return;
31514               }
31515               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
31516               el.fxDefaults = {stopFx: true};
31517               // maximum custom styling
31518               //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>');
31519               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>');              
31520               tipTitle = el.child('h3');
31521               tipTitle.enableDisplayMode("block");
31522               tipBody = el.child('div.x-tip-bd');
31523               tipBodyText = el.child('div.x-tip-bd-inner');
31524               //bdLeft = el.child('div.x-tip-bd-left');
31525               //bdRight = el.child('div.x-tip-bd-right');
31526               close = el.child('div.x-tip-close');
31527               close.enableDisplayMode("block");
31528               close.on("click", hide);
31529               var d = Roo.get(document);
31530               d.on("mousedown", onDown);
31531               d.on("mouseover", onOver);
31532               d.on("mouseout", onOut);
31533               d.on("mousemove", onMove);
31534               esc = d.addKeyListener(27, hide);
31535               esc.disable();
31536               if(Roo.dd.DD){
31537                   dd = el.initDD("default", null, {
31538                       onDrag : function(){
31539                           el.sync();  
31540                       }
31541                   });
31542                   dd.setHandleElId(tipTitle.id);
31543                   dd.lock();
31544               }
31545               inited = true;
31546           }
31547           this.enable(); 
31548        },
31549
31550     /**
31551      * Configures a new quick tip instance and assigns it to a target element.  The following config options
31552      * are supported:
31553      * <pre>
31554 Property    Type                   Description
31555 ----------  ---------------------  ------------------------------------------------------------------------
31556 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
31557      * </ul>
31558      * @param {Object} config The config object
31559      */
31560        register : function(config){
31561            var cs = config instanceof Array ? config : arguments;
31562            for(var i = 0, len = cs.length; i < len; i++) {
31563                var c = cs[i];
31564                var target = c.target;
31565                if(target){
31566                    if(target instanceof Array){
31567                        for(var j = 0, jlen = target.length; j < jlen; j++){
31568                            tagEls[target[j]] = c;
31569                        }
31570                    }else{
31571                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
31572                    }
31573                }
31574            }
31575        },
31576
31577     /**
31578      * Removes this quick tip from its element and destroys it.
31579      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
31580      */
31581        unregister : function(el){
31582            delete tagEls[Roo.id(el)];
31583        },
31584
31585     /**
31586      * Enable this quick tip.
31587      */
31588        enable : function(){
31589            if(inited && disabled){
31590                locks.pop();
31591                if(locks.length < 1){
31592                    disabled = false;
31593                }
31594            }
31595        },
31596
31597     /**
31598      * Disable this quick tip.
31599      */
31600        disable : function(){
31601           disabled = true;
31602           clearTimeout(showProc);
31603           clearTimeout(hideProc);
31604           clearTimeout(dismissProc);
31605           if(ce){
31606               hide(true);
31607           }
31608           locks.push(1);
31609        },
31610
31611     /**
31612      * Returns true if the quick tip is enabled, else false.
31613      */
31614        isEnabled : function(){
31615             return !disabled;
31616        },
31617
31618         // private
31619        tagConfig : {
31620            namespace : "ext",
31621            attribute : "qtip",
31622            width : "width",
31623            target : "target",
31624            title : "qtitle",
31625            hide : "hide",
31626            cls : "qclass"
31627        }
31628    };
31629 }();
31630
31631 // backwards compat
31632 Roo.QuickTips.tips = Roo.QuickTips.register;/*
31633  * Based on:
31634  * Ext JS Library 1.1.1
31635  * Copyright(c) 2006-2007, Ext JS, LLC.
31636  *
31637  * Originally Released Under LGPL - original licence link has changed is not relivant.
31638  *
31639  * Fork - LGPL
31640  * <script type="text/javascript">
31641  */
31642  
31643
31644 /**
31645  * @class Roo.tree.TreePanel
31646  * @extends Roo.data.Tree
31647
31648  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
31649  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
31650  * @cfg {Boolean} enableDD true to enable drag and drop
31651  * @cfg {Boolean} enableDrag true to enable just drag
31652  * @cfg {Boolean} enableDrop true to enable just drop
31653  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
31654  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
31655  * @cfg {String} ddGroup The DD group this TreePanel belongs to
31656  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
31657  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
31658  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
31659  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
31660  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
31661  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
31662  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
31663  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
31664  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
31665  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
31666  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
31667  * @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>
31668  * @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>
31669  * 
31670  * @constructor
31671  * @param {String/HTMLElement/Element} el The container element
31672  * @param {Object} config
31673  */
31674 Roo.tree.TreePanel = function(el, config){
31675     var root = false;
31676     var loader = false;
31677     if (config.root) {
31678         root = config.root;
31679         delete config.root;
31680     }
31681     if (config.loader) {
31682         loader = config.loader;
31683         delete config.loader;
31684     }
31685     
31686     Roo.apply(this, config);
31687     Roo.tree.TreePanel.superclass.constructor.call(this);
31688     this.el = Roo.get(el);
31689     this.el.addClass('x-tree');
31690     //console.log(root);
31691     if (root) {
31692         this.setRootNode( Roo.factory(root, Roo.tree));
31693     }
31694     if (loader) {
31695         this.loader = Roo.factory(loader, Roo.tree);
31696     }
31697    /**
31698     * Read-only. The id of the container element becomes this TreePanel's id.
31699     */
31700     this.id = this.el.id;
31701     this.addEvents({
31702         /**
31703         * @event beforeload
31704         * Fires before a node is loaded, return false to cancel
31705         * @param {Node} node The node being loaded
31706         */
31707         "beforeload" : true,
31708         /**
31709         * @event load
31710         * Fires when a node is loaded
31711         * @param {Node} node The node that was loaded
31712         */
31713         "load" : true,
31714         /**
31715         * @event textchange
31716         * Fires when the text for a node is changed
31717         * @param {Node} node The node
31718         * @param {String} text The new text
31719         * @param {String} oldText The old text
31720         */
31721         "textchange" : true,
31722         /**
31723         * @event beforeexpand
31724         * Fires before a node is expanded, return false to cancel.
31725         * @param {Node} node The node
31726         * @param {Boolean} deep
31727         * @param {Boolean} anim
31728         */
31729         "beforeexpand" : true,
31730         /**
31731         * @event beforecollapse
31732         * Fires before a node is collapsed, return false to cancel.
31733         * @param {Node} node The node
31734         * @param {Boolean} deep
31735         * @param {Boolean} anim
31736         */
31737         "beforecollapse" : true,
31738         /**
31739         * @event expand
31740         * Fires when a node is expanded
31741         * @param {Node} node The node
31742         */
31743         "expand" : true,
31744         /**
31745         * @event disabledchange
31746         * Fires when the disabled status of a node changes
31747         * @param {Node} node The node
31748         * @param {Boolean} disabled
31749         */
31750         "disabledchange" : true,
31751         /**
31752         * @event collapse
31753         * Fires when a node is collapsed
31754         * @param {Node} node The node
31755         */
31756         "collapse" : true,
31757         /**
31758         * @event beforeclick
31759         * Fires before click processing on a node. Return false to cancel the default action.
31760         * @param {Node} node The node
31761         * @param {Roo.EventObject} e The event object
31762         */
31763         "beforeclick":true,
31764         /**
31765         * @event checkchange
31766         * Fires when a node with a checkbox's checked property changes
31767         * @param {Node} this This node
31768         * @param {Boolean} checked
31769         */
31770         "checkchange":true,
31771         /**
31772         * @event click
31773         * Fires when a node is clicked
31774         * @param {Node} node The node
31775         * @param {Roo.EventObject} e The event object
31776         */
31777         "click":true,
31778         /**
31779         * @event dblclick
31780         * Fires when a node is double clicked
31781         * @param {Node} node The node
31782         * @param {Roo.EventObject} e The event object
31783         */
31784         "dblclick":true,
31785         /**
31786         * @event contextmenu
31787         * Fires when a node is right clicked
31788         * @param {Node} node The node
31789         * @param {Roo.EventObject} e The event object
31790         */
31791         "contextmenu":true,
31792         /**
31793         * @event beforechildrenrendered
31794         * Fires right before the child nodes for a node are rendered
31795         * @param {Node} node The node
31796         */
31797         "beforechildrenrendered":true,
31798         /**
31799         * @event startdrag
31800         * Fires when a node starts being dragged
31801         * @param {Roo.tree.TreePanel} this
31802         * @param {Roo.tree.TreeNode} node
31803         * @param {event} e The raw browser event
31804         */ 
31805        "startdrag" : true,
31806        /**
31807         * @event enddrag
31808         * Fires when a drag operation is complete
31809         * @param {Roo.tree.TreePanel} this
31810         * @param {Roo.tree.TreeNode} node
31811         * @param {event} e The raw browser event
31812         */
31813        "enddrag" : true,
31814        /**
31815         * @event dragdrop
31816         * Fires when a dragged node is dropped on a valid DD target
31817         * @param {Roo.tree.TreePanel} this
31818         * @param {Roo.tree.TreeNode} node
31819         * @param {DD} dd The dd it was dropped on
31820         * @param {event} e The raw browser event
31821         */
31822        "dragdrop" : true,
31823        /**
31824         * @event beforenodedrop
31825         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31826         * passed to handlers has the following properties:<br />
31827         * <ul style="padding:5px;padding-left:16px;">
31828         * <li>tree - The TreePanel</li>
31829         * <li>target - The node being targeted for the drop</li>
31830         * <li>data - The drag data from the drag source</li>
31831         * <li>point - The point of the drop - append, above or below</li>
31832         * <li>source - The drag source</li>
31833         * <li>rawEvent - Raw mouse event</li>
31834         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31835         * to be inserted by setting them on this object.</li>
31836         * <li>cancel - Set this to true to cancel the drop.</li>
31837         * </ul>
31838         * @param {Object} dropEvent
31839         */
31840        "beforenodedrop" : true,
31841        /**
31842         * @event nodedrop
31843         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31844         * passed to handlers has the following properties:<br />
31845         * <ul style="padding:5px;padding-left:16px;">
31846         * <li>tree - The TreePanel</li>
31847         * <li>target - The node being targeted for the drop</li>
31848         * <li>data - The drag data from the drag source</li>
31849         * <li>point - The point of the drop - append, above or below</li>
31850         * <li>source - The drag source</li>
31851         * <li>rawEvent - Raw mouse event</li>
31852         * <li>dropNode - Dropped node(s).</li>
31853         * </ul>
31854         * @param {Object} dropEvent
31855         */
31856        "nodedrop" : true,
31857         /**
31858         * @event nodedragover
31859         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31860         * passed to handlers has the following properties:<br />
31861         * <ul style="padding:5px;padding-left:16px;">
31862         * <li>tree - The TreePanel</li>
31863         * <li>target - The node being targeted for the drop</li>
31864         * <li>data - The drag data from the drag source</li>
31865         * <li>point - The point of the drop - append, above or below</li>
31866         * <li>source - The drag source</li>
31867         * <li>rawEvent - Raw mouse event</li>
31868         * <li>dropNode - Drop node(s) provided by the source.</li>
31869         * <li>cancel - Set this to true to signal drop not allowed.</li>
31870         * </ul>
31871         * @param {Object} dragOverEvent
31872         */
31873        "nodedragover" : true
31874         
31875     });
31876     if(this.singleExpand){
31877        this.on("beforeexpand", this.restrictExpand, this);
31878     }
31879     if (this.editor) {
31880         this.editor.tree = this;
31881         this.editor = Roo.factory(this.editor, Roo.tree);
31882     }
31883     
31884     if (this.selModel) {
31885         this.selModel = Roo.factory(this.selModel, Roo.tree);
31886     }
31887    
31888 };
31889 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31890     rootVisible : true,
31891     animate: Roo.enableFx,
31892     lines : true,
31893     enableDD : false,
31894     hlDrop : Roo.enableFx,
31895   
31896     renderer: false,
31897     
31898     rendererTip: false,
31899     // private
31900     restrictExpand : function(node){
31901         var p = node.parentNode;
31902         if(p){
31903             if(p.expandedChild && p.expandedChild.parentNode == p){
31904                 p.expandedChild.collapse();
31905             }
31906             p.expandedChild = node;
31907         }
31908     },
31909
31910     // private override
31911     setRootNode : function(node){
31912         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31913         if(!this.rootVisible){
31914             node.ui = new Roo.tree.RootTreeNodeUI(node);
31915         }
31916         return node;
31917     },
31918
31919     /**
31920      * Returns the container element for this TreePanel
31921      */
31922     getEl : function(){
31923         return this.el;
31924     },
31925
31926     /**
31927      * Returns the default TreeLoader for this TreePanel
31928      */
31929     getLoader : function(){
31930         return this.loader;
31931     },
31932
31933     /**
31934      * Expand all nodes
31935      */
31936     expandAll : function(){
31937         this.root.expand(true);
31938     },
31939
31940     /**
31941      * Collapse all nodes
31942      */
31943     collapseAll : function(){
31944         this.root.collapse(true);
31945     },
31946
31947     /**
31948      * Returns the selection model used by this TreePanel
31949      */
31950     getSelectionModel : function(){
31951         if(!this.selModel){
31952             this.selModel = new Roo.tree.DefaultSelectionModel();
31953         }
31954         return this.selModel;
31955     },
31956
31957     /**
31958      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31959      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31960      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31961      * @return {Array}
31962      */
31963     getChecked : function(a, startNode){
31964         startNode = startNode || this.root;
31965         var r = [];
31966         var f = function(){
31967             if(this.attributes.checked){
31968                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31969             }
31970         }
31971         startNode.cascade(f);
31972         return r;
31973     },
31974
31975     /**
31976      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31977      * @param {String} path
31978      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31979      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31980      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31981      */
31982     expandPath : function(path, attr, callback){
31983         attr = attr || "id";
31984         var keys = path.split(this.pathSeparator);
31985         var curNode = this.root;
31986         if(curNode.attributes[attr] != keys[1]){ // invalid root
31987             if(callback){
31988                 callback(false, null);
31989             }
31990             return;
31991         }
31992         var index = 1;
31993         var f = function(){
31994             if(++index == keys.length){
31995                 if(callback){
31996                     callback(true, curNode);
31997                 }
31998                 return;
31999             }
32000             var c = curNode.findChild(attr, keys[index]);
32001             if(!c){
32002                 if(callback){
32003                     callback(false, curNode);
32004                 }
32005                 return;
32006             }
32007             curNode = c;
32008             c.expand(false, false, f);
32009         };
32010         curNode.expand(false, false, f);
32011     },
32012
32013     /**
32014      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32015      * @param {String} path
32016      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32017      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32018      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32019      */
32020     selectPath : function(path, attr, callback){
32021         attr = attr || "id";
32022         var keys = path.split(this.pathSeparator);
32023         var v = keys.pop();
32024         if(keys.length > 0){
32025             var f = function(success, node){
32026                 if(success && node){
32027                     var n = node.findChild(attr, v);
32028                     if(n){
32029                         n.select();
32030                         if(callback){
32031                             callback(true, n);
32032                         }
32033                     }else if(callback){
32034                         callback(false, n);
32035                     }
32036                 }else{
32037                     if(callback){
32038                         callback(false, n);
32039                     }
32040                 }
32041             };
32042             this.expandPath(keys.join(this.pathSeparator), attr, f);
32043         }else{
32044             this.root.select();
32045             if(callback){
32046                 callback(true, this.root);
32047             }
32048         }
32049     },
32050
32051     getTreeEl : function(){
32052         return this.el;
32053     },
32054
32055     /**
32056      * Trigger rendering of this TreePanel
32057      */
32058     render : function(){
32059         if (this.innerCt) {
32060             return this; // stop it rendering more than once!!
32061         }
32062         
32063         this.innerCt = this.el.createChild({tag:"ul",
32064                cls:"x-tree-root-ct " +
32065                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32066
32067         if(this.containerScroll){
32068             Roo.dd.ScrollManager.register(this.el);
32069         }
32070         if((this.enableDD || this.enableDrop) && !this.dropZone){
32071            /**
32072             * The dropZone used by this tree if drop is enabled
32073             * @type Roo.tree.TreeDropZone
32074             */
32075              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32076                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32077            });
32078         }
32079         if((this.enableDD || this.enableDrag) && !this.dragZone){
32080            /**
32081             * The dragZone used by this tree if drag is enabled
32082             * @type Roo.tree.TreeDragZone
32083             */
32084             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32085                ddGroup: this.ddGroup || "TreeDD",
32086                scroll: this.ddScroll
32087            });
32088         }
32089         this.getSelectionModel().init(this);
32090         if (!this.root) {
32091             Roo.log("ROOT not set in tree");
32092             return this;
32093         }
32094         this.root.render();
32095         if(!this.rootVisible){
32096             this.root.renderChildren();
32097         }
32098         return this;
32099     }
32100 });/*
32101  * Based on:
32102  * Ext JS Library 1.1.1
32103  * Copyright(c) 2006-2007, Ext JS, LLC.
32104  *
32105  * Originally Released Under LGPL - original licence link has changed is not relivant.
32106  *
32107  * Fork - LGPL
32108  * <script type="text/javascript">
32109  */
32110  
32111
32112 /**
32113  * @class Roo.tree.DefaultSelectionModel
32114  * @extends Roo.util.Observable
32115  * The default single selection for a TreePanel.
32116  * @param {Object} cfg Configuration
32117  */
32118 Roo.tree.DefaultSelectionModel = function(cfg){
32119    this.selNode = null;
32120    
32121    
32122    
32123    this.addEvents({
32124        /**
32125         * @event selectionchange
32126         * Fires when the selected node changes
32127         * @param {DefaultSelectionModel} this
32128         * @param {TreeNode} node the new selection
32129         */
32130        "selectionchange" : true,
32131
32132        /**
32133         * @event beforeselect
32134         * Fires before the selected node changes, return false to cancel the change
32135         * @param {DefaultSelectionModel} this
32136         * @param {TreeNode} node the new selection
32137         * @param {TreeNode} node the old selection
32138         */
32139        "beforeselect" : true
32140    });
32141    
32142     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32143 };
32144
32145 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32146     init : function(tree){
32147         this.tree = tree;
32148         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32149         tree.on("click", this.onNodeClick, this);
32150     },
32151     
32152     onNodeClick : function(node, e){
32153         if (e.ctrlKey && this.selNode == node)  {
32154             this.unselect(node);
32155             return;
32156         }
32157         this.select(node);
32158     },
32159     
32160     /**
32161      * Select a node.
32162      * @param {TreeNode} node The node to select
32163      * @return {TreeNode} The selected node
32164      */
32165     select : function(node){
32166         var last = this.selNode;
32167         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32168             if(last){
32169                 last.ui.onSelectedChange(false);
32170             }
32171             this.selNode = node;
32172             node.ui.onSelectedChange(true);
32173             this.fireEvent("selectionchange", this, node, last);
32174         }
32175         return node;
32176     },
32177     
32178     /**
32179      * Deselect a node.
32180      * @param {TreeNode} node The node to unselect
32181      */
32182     unselect : function(node){
32183         if(this.selNode == node){
32184             this.clearSelections();
32185         }    
32186     },
32187     
32188     /**
32189      * Clear all selections
32190      */
32191     clearSelections : function(){
32192         var n = this.selNode;
32193         if(n){
32194             n.ui.onSelectedChange(false);
32195             this.selNode = null;
32196             this.fireEvent("selectionchange", this, null);
32197         }
32198         return n;
32199     },
32200     
32201     /**
32202      * Get the selected node
32203      * @return {TreeNode} The selected node
32204      */
32205     getSelectedNode : function(){
32206         return this.selNode;    
32207     },
32208     
32209     /**
32210      * Returns true if the node is selected
32211      * @param {TreeNode} node The node to check
32212      * @return {Boolean}
32213      */
32214     isSelected : function(node){
32215         return this.selNode == node;  
32216     },
32217
32218     /**
32219      * Selects the node above the selected node in the tree, intelligently walking the nodes
32220      * @return TreeNode The new selection
32221      */
32222     selectPrevious : function(){
32223         var s = this.selNode || this.lastSelNode;
32224         if(!s){
32225             return null;
32226         }
32227         var ps = s.previousSibling;
32228         if(ps){
32229             if(!ps.isExpanded() || ps.childNodes.length < 1){
32230                 return this.select(ps);
32231             } else{
32232                 var lc = ps.lastChild;
32233                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32234                     lc = lc.lastChild;
32235                 }
32236                 return this.select(lc);
32237             }
32238         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32239             return this.select(s.parentNode);
32240         }
32241         return null;
32242     },
32243
32244     /**
32245      * Selects the node above the selected node in the tree, intelligently walking the nodes
32246      * @return TreeNode The new selection
32247      */
32248     selectNext : function(){
32249         var s = this.selNode || this.lastSelNode;
32250         if(!s){
32251             return null;
32252         }
32253         if(s.firstChild && s.isExpanded()){
32254              return this.select(s.firstChild);
32255          }else if(s.nextSibling){
32256              return this.select(s.nextSibling);
32257          }else if(s.parentNode){
32258             var newS = null;
32259             s.parentNode.bubble(function(){
32260                 if(this.nextSibling){
32261                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32262                     return false;
32263                 }
32264             });
32265             return newS;
32266          }
32267         return null;
32268     },
32269
32270     onKeyDown : function(e){
32271         var s = this.selNode || this.lastSelNode;
32272         // undesirable, but required
32273         var sm = this;
32274         if(!s){
32275             return;
32276         }
32277         var k = e.getKey();
32278         switch(k){
32279              case e.DOWN:
32280                  e.stopEvent();
32281                  this.selectNext();
32282              break;
32283              case e.UP:
32284                  e.stopEvent();
32285                  this.selectPrevious();
32286              break;
32287              case e.RIGHT:
32288                  e.preventDefault();
32289                  if(s.hasChildNodes()){
32290                      if(!s.isExpanded()){
32291                          s.expand();
32292                      }else if(s.firstChild){
32293                          this.select(s.firstChild, e);
32294                      }
32295                  }
32296              break;
32297              case e.LEFT:
32298                  e.preventDefault();
32299                  if(s.hasChildNodes() && s.isExpanded()){
32300                      s.collapse();
32301                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32302                      this.select(s.parentNode, e);
32303                  }
32304              break;
32305         };
32306     }
32307 });
32308
32309 /**
32310  * @class Roo.tree.MultiSelectionModel
32311  * @extends Roo.util.Observable
32312  * Multi selection for a TreePanel.
32313  * @param {Object} cfg Configuration
32314  */
32315 Roo.tree.MultiSelectionModel = function(){
32316    this.selNodes = [];
32317    this.selMap = {};
32318    this.addEvents({
32319        /**
32320         * @event selectionchange
32321         * Fires when the selected nodes change
32322         * @param {MultiSelectionModel} this
32323         * @param {Array} nodes Array of the selected nodes
32324         */
32325        "selectionchange" : true
32326    });
32327    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32328    
32329 };
32330
32331 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32332     init : function(tree){
32333         this.tree = tree;
32334         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32335         tree.on("click", this.onNodeClick, this);
32336     },
32337     
32338     onNodeClick : function(node, e){
32339         this.select(node, e, e.ctrlKey);
32340     },
32341     
32342     /**
32343      * Select a node.
32344      * @param {TreeNode} node The node to select
32345      * @param {EventObject} e (optional) An event associated with the selection
32346      * @param {Boolean} keepExisting True to retain existing selections
32347      * @return {TreeNode} The selected node
32348      */
32349     select : function(node, e, keepExisting){
32350         if(keepExisting !== true){
32351             this.clearSelections(true);
32352         }
32353         if(this.isSelected(node)){
32354             this.lastSelNode = node;
32355             return node;
32356         }
32357         this.selNodes.push(node);
32358         this.selMap[node.id] = node;
32359         this.lastSelNode = node;
32360         node.ui.onSelectedChange(true);
32361         this.fireEvent("selectionchange", this, this.selNodes);
32362         return node;
32363     },
32364     
32365     /**
32366      * Deselect a node.
32367      * @param {TreeNode} node The node to unselect
32368      */
32369     unselect : function(node){
32370         if(this.selMap[node.id]){
32371             node.ui.onSelectedChange(false);
32372             var sn = this.selNodes;
32373             var index = -1;
32374             if(sn.indexOf){
32375                 index = sn.indexOf(node);
32376             }else{
32377                 for(var i = 0, len = sn.length; i < len; i++){
32378                     if(sn[i] == node){
32379                         index = i;
32380                         break;
32381                     }
32382                 }
32383             }
32384             if(index != -1){
32385                 this.selNodes.splice(index, 1);
32386             }
32387             delete this.selMap[node.id];
32388             this.fireEvent("selectionchange", this, this.selNodes);
32389         }
32390     },
32391     
32392     /**
32393      * Clear all selections
32394      */
32395     clearSelections : function(suppressEvent){
32396         var sn = this.selNodes;
32397         if(sn.length > 0){
32398             for(var i = 0, len = sn.length; i < len; i++){
32399                 sn[i].ui.onSelectedChange(false);
32400             }
32401             this.selNodes = [];
32402             this.selMap = {};
32403             if(suppressEvent !== true){
32404                 this.fireEvent("selectionchange", this, this.selNodes);
32405             }
32406         }
32407     },
32408     
32409     /**
32410      * Returns true if the node is selected
32411      * @param {TreeNode} node The node to check
32412      * @return {Boolean}
32413      */
32414     isSelected : function(node){
32415         return this.selMap[node.id] ? true : false;  
32416     },
32417     
32418     /**
32419      * Returns an array of the selected nodes
32420      * @return {Array}
32421      */
32422     getSelectedNodes : function(){
32423         return this.selNodes;    
32424     },
32425
32426     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32427
32428     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32429
32430     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32431 });/*
32432  * Based on:
32433  * Ext JS Library 1.1.1
32434  * Copyright(c) 2006-2007, Ext JS, LLC.
32435  *
32436  * Originally Released Under LGPL - original licence link has changed is not relivant.
32437  *
32438  * Fork - LGPL
32439  * <script type="text/javascript">
32440  */
32441  
32442 /**
32443  * @class Roo.tree.TreeNode
32444  * @extends Roo.data.Node
32445  * @cfg {String} text The text for this node
32446  * @cfg {Boolean} expanded true to start the node expanded
32447  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32448  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32449  * @cfg {Boolean} disabled true to start the node disabled
32450  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32451  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32452  * @cfg {String} cls A css class to be added to the node
32453  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32454  * @cfg {String} href URL of the link used for the node (defaults to #)
32455  * @cfg {String} hrefTarget target frame for the link
32456  * @cfg {String} qtip An Ext QuickTip for the node
32457  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32458  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32459  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32460  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32461  * (defaults to undefined with no checkbox rendered)
32462  * @constructor
32463  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32464  */
32465 Roo.tree.TreeNode = function(attributes){
32466     attributes = attributes || {};
32467     if(typeof attributes == "string"){
32468         attributes = {text: attributes};
32469     }
32470     this.childrenRendered = false;
32471     this.rendered = false;
32472     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32473     this.expanded = attributes.expanded === true;
32474     this.isTarget = attributes.isTarget !== false;
32475     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32476     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32477
32478     /**
32479      * Read-only. The text for this node. To change it use setText().
32480      * @type String
32481      */
32482     this.text = attributes.text;
32483     /**
32484      * True if this node is disabled.
32485      * @type Boolean
32486      */
32487     this.disabled = attributes.disabled === true;
32488
32489     this.addEvents({
32490         /**
32491         * @event textchange
32492         * Fires when the text for this node is changed
32493         * @param {Node} this This node
32494         * @param {String} text The new text
32495         * @param {String} oldText The old text
32496         */
32497         "textchange" : true,
32498         /**
32499         * @event beforeexpand
32500         * Fires before this node is expanded, return false to cancel.
32501         * @param {Node} this This node
32502         * @param {Boolean} deep
32503         * @param {Boolean} anim
32504         */
32505         "beforeexpand" : true,
32506         /**
32507         * @event beforecollapse
32508         * Fires before this node is collapsed, return false to cancel.
32509         * @param {Node} this This node
32510         * @param {Boolean} deep
32511         * @param {Boolean} anim
32512         */
32513         "beforecollapse" : true,
32514         /**
32515         * @event expand
32516         * Fires when this node is expanded
32517         * @param {Node} this This node
32518         */
32519         "expand" : true,
32520         /**
32521         * @event disabledchange
32522         * Fires when the disabled status of this node changes
32523         * @param {Node} this This node
32524         * @param {Boolean} disabled
32525         */
32526         "disabledchange" : true,
32527         /**
32528         * @event collapse
32529         * Fires when this node is collapsed
32530         * @param {Node} this This node
32531         */
32532         "collapse" : true,
32533         /**
32534         * @event beforeclick
32535         * Fires before click processing. Return false to cancel the default action.
32536         * @param {Node} this This node
32537         * @param {Roo.EventObject} e The event object
32538         */
32539         "beforeclick":true,
32540         /**
32541         * @event checkchange
32542         * Fires when a node with a checkbox's checked property changes
32543         * @param {Node} this This node
32544         * @param {Boolean} checked
32545         */
32546         "checkchange":true,
32547         /**
32548         * @event click
32549         * Fires when this node is clicked
32550         * @param {Node} this This node
32551         * @param {Roo.EventObject} e The event object
32552         */
32553         "click":true,
32554         /**
32555         * @event dblclick
32556         * Fires when this node is double clicked
32557         * @param {Node} this This node
32558         * @param {Roo.EventObject} e The event object
32559         */
32560         "dblclick":true,
32561         /**
32562         * @event contextmenu
32563         * Fires when this node is right clicked
32564         * @param {Node} this This node
32565         * @param {Roo.EventObject} e The event object
32566         */
32567         "contextmenu":true,
32568         /**
32569         * @event beforechildrenrendered
32570         * Fires right before the child nodes for this node are rendered
32571         * @param {Node} this This node
32572         */
32573         "beforechildrenrendered":true
32574     });
32575
32576     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
32577
32578     /**
32579      * Read-only. The UI for this node
32580      * @type TreeNodeUI
32581      */
32582     this.ui = new uiClass(this);
32583     
32584     // finally support items[]
32585     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
32586         return;
32587     }
32588     
32589     
32590     Roo.each(this.attributes.items, function(c) {
32591         this.appendChild(Roo.factory(c,Roo.Tree));
32592     }, this);
32593     delete this.attributes.items;
32594     
32595     
32596     
32597 };
32598 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
32599     preventHScroll: true,
32600     /**
32601      * Returns true if this node is expanded
32602      * @return {Boolean}
32603      */
32604     isExpanded : function(){
32605         return this.expanded;
32606     },
32607
32608     /**
32609      * Returns the UI object for this node
32610      * @return {TreeNodeUI}
32611      */
32612     getUI : function(){
32613         return this.ui;
32614     },
32615
32616     // private override
32617     setFirstChild : function(node){
32618         var of = this.firstChild;
32619         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
32620         if(this.childrenRendered && of && node != of){
32621             of.renderIndent(true, true);
32622         }
32623         if(this.rendered){
32624             this.renderIndent(true, true);
32625         }
32626     },
32627
32628     // private override
32629     setLastChild : function(node){
32630         var ol = this.lastChild;
32631         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
32632         if(this.childrenRendered && ol && node != ol){
32633             ol.renderIndent(true, true);
32634         }
32635         if(this.rendered){
32636             this.renderIndent(true, true);
32637         }
32638     },
32639
32640     // these methods are overridden to provide lazy rendering support
32641     // private override
32642     appendChild : function()
32643     {
32644         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
32645         if(node && this.childrenRendered){
32646             node.render();
32647         }
32648         this.ui.updateExpandIcon();
32649         return node;
32650     },
32651
32652     // private override
32653     removeChild : function(node){
32654         this.ownerTree.getSelectionModel().unselect(node);
32655         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
32656         // if it's been rendered remove dom node
32657         if(this.childrenRendered){
32658             node.ui.remove();
32659         }
32660         if(this.childNodes.length < 1){
32661             this.collapse(false, false);
32662         }else{
32663             this.ui.updateExpandIcon();
32664         }
32665         if(!this.firstChild) {
32666             this.childrenRendered = false;
32667         }
32668         return node;
32669     },
32670
32671     // private override
32672     insertBefore : function(node, refNode){
32673         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
32674         if(newNode && refNode && this.childrenRendered){
32675             node.render();
32676         }
32677         this.ui.updateExpandIcon();
32678         return newNode;
32679     },
32680
32681     /**
32682      * Sets the text for this node
32683      * @param {String} text
32684      */
32685     setText : function(text){
32686         var oldText = this.text;
32687         this.text = text;
32688         this.attributes.text = text;
32689         if(this.rendered){ // event without subscribing
32690             this.ui.onTextChange(this, text, oldText);
32691         }
32692         this.fireEvent("textchange", this, text, oldText);
32693     },
32694
32695     /**
32696      * Triggers selection of this node
32697      */
32698     select : function(){
32699         this.getOwnerTree().getSelectionModel().select(this);
32700     },
32701
32702     /**
32703      * Triggers deselection of this node
32704      */
32705     unselect : function(){
32706         this.getOwnerTree().getSelectionModel().unselect(this);
32707     },
32708
32709     /**
32710      * Returns true if this node is selected
32711      * @return {Boolean}
32712      */
32713     isSelected : function(){
32714         return this.getOwnerTree().getSelectionModel().isSelected(this);
32715     },
32716
32717     /**
32718      * Expand this node.
32719      * @param {Boolean} deep (optional) True to expand all children as well
32720      * @param {Boolean} anim (optional) false to cancel the default animation
32721      * @param {Function} callback (optional) A callback to be called when
32722      * expanding this node completes (does not wait for deep expand to complete).
32723      * Called with 1 parameter, this node.
32724      */
32725     expand : function(deep, anim, callback){
32726         if(!this.expanded){
32727             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
32728                 return;
32729             }
32730             if(!this.childrenRendered){
32731                 this.renderChildren();
32732             }
32733             this.expanded = true;
32734             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
32735                 this.ui.animExpand(function(){
32736                     this.fireEvent("expand", this);
32737                     if(typeof callback == "function"){
32738                         callback(this);
32739                     }
32740                     if(deep === true){
32741                         this.expandChildNodes(true);
32742                     }
32743                 }.createDelegate(this));
32744                 return;
32745             }else{
32746                 this.ui.expand();
32747                 this.fireEvent("expand", this);
32748                 if(typeof callback == "function"){
32749                     callback(this);
32750                 }
32751             }
32752         }else{
32753            if(typeof callback == "function"){
32754                callback(this);
32755            }
32756         }
32757         if(deep === true){
32758             this.expandChildNodes(true);
32759         }
32760     },
32761
32762     isHiddenRoot : function(){
32763         return this.isRoot && !this.getOwnerTree().rootVisible;
32764     },
32765
32766     /**
32767      * Collapse this node.
32768      * @param {Boolean} deep (optional) True to collapse all children as well
32769      * @param {Boolean} anim (optional) false to cancel the default animation
32770      */
32771     collapse : function(deep, anim){
32772         if(this.expanded && !this.isHiddenRoot()){
32773             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32774                 return;
32775             }
32776             this.expanded = false;
32777             if((this.getOwnerTree().animate && anim !== false) || anim){
32778                 this.ui.animCollapse(function(){
32779                     this.fireEvent("collapse", this);
32780                     if(deep === true){
32781                         this.collapseChildNodes(true);
32782                     }
32783                 }.createDelegate(this));
32784                 return;
32785             }else{
32786                 this.ui.collapse();
32787                 this.fireEvent("collapse", this);
32788             }
32789         }
32790         if(deep === true){
32791             var cs = this.childNodes;
32792             for(var i = 0, len = cs.length; i < len; i++) {
32793                 cs[i].collapse(true, false);
32794             }
32795         }
32796     },
32797
32798     // private
32799     delayedExpand : function(delay){
32800         if(!this.expandProcId){
32801             this.expandProcId = this.expand.defer(delay, this);
32802         }
32803     },
32804
32805     // private
32806     cancelExpand : function(){
32807         if(this.expandProcId){
32808             clearTimeout(this.expandProcId);
32809         }
32810         this.expandProcId = false;
32811     },
32812
32813     /**
32814      * Toggles expanded/collapsed state of the node
32815      */
32816     toggle : function(){
32817         if(this.expanded){
32818             this.collapse();
32819         }else{
32820             this.expand();
32821         }
32822     },
32823
32824     /**
32825      * Ensures all parent nodes are expanded
32826      */
32827     ensureVisible : function(callback){
32828         var tree = this.getOwnerTree();
32829         tree.expandPath(this.parentNode.getPath(), false, function(){
32830             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32831             Roo.callback(callback);
32832         }.createDelegate(this));
32833     },
32834
32835     /**
32836      * Expand all child nodes
32837      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32838      */
32839     expandChildNodes : function(deep){
32840         var cs = this.childNodes;
32841         for(var i = 0, len = cs.length; i < len; i++) {
32842                 cs[i].expand(deep);
32843         }
32844     },
32845
32846     /**
32847      * Collapse all child nodes
32848      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32849      */
32850     collapseChildNodes : function(deep){
32851         var cs = this.childNodes;
32852         for(var i = 0, len = cs.length; i < len; i++) {
32853                 cs[i].collapse(deep);
32854         }
32855     },
32856
32857     /**
32858      * Disables this node
32859      */
32860     disable : function(){
32861         this.disabled = true;
32862         this.unselect();
32863         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32864             this.ui.onDisableChange(this, true);
32865         }
32866         this.fireEvent("disabledchange", this, true);
32867     },
32868
32869     /**
32870      * Enables this node
32871      */
32872     enable : function(){
32873         this.disabled = false;
32874         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32875             this.ui.onDisableChange(this, false);
32876         }
32877         this.fireEvent("disabledchange", this, false);
32878     },
32879
32880     // private
32881     renderChildren : function(suppressEvent){
32882         if(suppressEvent !== false){
32883             this.fireEvent("beforechildrenrendered", this);
32884         }
32885         var cs = this.childNodes;
32886         for(var i = 0, len = cs.length; i < len; i++){
32887             cs[i].render(true);
32888         }
32889         this.childrenRendered = true;
32890     },
32891
32892     // private
32893     sort : function(fn, scope){
32894         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32895         if(this.childrenRendered){
32896             var cs = this.childNodes;
32897             for(var i = 0, len = cs.length; i < len; i++){
32898                 cs[i].render(true);
32899             }
32900         }
32901     },
32902
32903     // private
32904     render : function(bulkRender){
32905         this.ui.render(bulkRender);
32906         if(!this.rendered){
32907             this.rendered = true;
32908             if(this.expanded){
32909                 this.expanded = false;
32910                 this.expand(false, false);
32911             }
32912         }
32913     },
32914
32915     // private
32916     renderIndent : function(deep, refresh){
32917         if(refresh){
32918             this.ui.childIndent = null;
32919         }
32920         this.ui.renderIndent();
32921         if(deep === true && this.childrenRendered){
32922             var cs = this.childNodes;
32923             for(var i = 0, len = cs.length; i < len; i++){
32924                 cs[i].renderIndent(true, refresh);
32925             }
32926         }
32927     }
32928 });/*
32929  * Based on:
32930  * Ext JS Library 1.1.1
32931  * Copyright(c) 2006-2007, Ext JS, LLC.
32932  *
32933  * Originally Released Under LGPL - original licence link has changed is not relivant.
32934  *
32935  * Fork - LGPL
32936  * <script type="text/javascript">
32937  */
32938  
32939 /**
32940  * @class Roo.tree.AsyncTreeNode
32941  * @extends Roo.tree.TreeNode
32942  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32943  * @constructor
32944  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32945  */
32946  Roo.tree.AsyncTreeNode = function(config){
32947     this.loaded = false;
32948     this.loading = false;
32949     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32950     /**
32951     * @event beforeload
32952     * Fires before this node is loaded, return false to cancel
32953     * @param {Node} this This node
32954     */
32955     this.addEvents({'beforeload':true, 'load': true});
32956     /**
32957     * @event load
32958     * Fires when this node is loaded
32959     * @param {Node} this This node
32960     */
32961     /**
32962      * The loader used by this node (defaults to using the tree's defined loader)
32963      * @type TreeLoader
32964      * @property loader
32965      */
32966 };
32967 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32968     expand : function(deep, anim, callback){
32969         if(this.loading){ // if an async load is already running, waiting til it's done
32970             var timer;
32971             var f = function(){
32972                 if(!this.loading){ // done loading
32973                     clearInterval(timer);
32974                     this.expand(deep, anim, callback);
32975                 }
32976             }.createDelegate(this);
32977             timer = setInterval(f, 200);
32978             return;
32979         }
32980         if(!this.loaded){
32981             if(this.fireEvent("beforeload", this) === false){
32982                 return;
32983             }
32984             this.loading = true;
32985             this.ui.beforeLoad(this);
32986             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32987             if(loader){
32988                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32989                 return;
32990             }
32991         }
32992         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32993     },
32994     
32995     /**
32996      * Returns true if this node is currently loading
32997      * @return {Boolean}
32998      */
32999     isLoading : function(){
33000         return this.loading;  
33001     },
33002     
33003     loadComplete : function(deep, anim, callback){
33004         this.loading = false;
33005         this.loaded = true;
33006         this.ui.afterLoad(this);
33007         this.fireEvent("load", this);
33008         this.expand(deep, anim, callback);
33009     },
33010     
33011     /**
33012      * Returns true if this node has been loaded
33013      * @return {Boolean}
33014      */
33015     isLoaded : function(){
33016         return this.loaded;
33017     },
33018     
33019     hasChildNodes : function(){
33020         if(!this.isLeaf() && !this.loaded){
33021             return true;
33022         }else{
33023             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33024         }
33025     },
33026
33027     /**
33028      * Trigger a reload for this node
33029      * @param {Function} callback
33030      */
33031     reload : function(callback){
33032         this.collapse(false, false);
33033         while(this.firstChild){
33034             this.removeChild(this.firstChild);
33035         }
33036         this.childrenRendered = false;
33037         this.loaded = false;
33038         if(this.isHiddenRoot()){
33039             this.expanded = false;
33040         }
33041         this.expand(false, false, callback);
33042     }
33043 });/*
33044  * Based on:
33045  * Ext JS Library 1.1.1
33046  * Copyright(c) 2006-2007, Ext JS, LLC.
33047  *
33048  * Originally Released Under LGPL - original licence link has changed is not relivant.
33049  *
33050  * Fork - LGPL
33051  * <script type="text/javascript">
33052  */
33053  
33054 /**
33055  * @class Roo.tree.TreeNodeUI
33056  * @constructor
33057  * @param {Object} node The node to render
33058  * The TreeNode UI implementation is separate from the
33059  * tree implementation. Unless you are customizing the tree UI,
33060  * you should never have to use this directly.
33061  */
33062 Roo.tree.TreeNodeUI = function(node){
33063     this.node = node;
33064     this.rendered = false;
33065     this.animating = false;
33066     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33067 };
33068
33069 Roo.tree.TreeNodeUI.prototype = {
33070     removeChild : function(node){
33071         if(this.rendered){
33072             this.ctNode.removeChild(node.ui.getEl());
33073         }
33074     },
33075
33076     beforeLoad : function(){
33077          this.addClass("x-tree-node-loading");
33078     },
33079
33080     afterLoad : function(){
33081          this.removeClass("x-tree-node-loading");
33082     },
33083
33084     onTextChange : function(node, text, oldText){
33085         if(this.rendered){
33086             this.textNode.innerHTML = text;
33087         }
33088     },
33089
33090     onDisableChange : function(node, state){
33091         this.disabled = state;
33092         if(state){
33093             this.addClass("x-tree-node-disabled");
33094         }else{
33095             this.removeClass("x-tree-node-disabled");
33096         }
33097     },
33098
33099     onSelectedChange : function(state){
33100         if(state){
33101             this.focus();
33102             this.addClass("x-tree-selected");
33103         }else{
33104             //this.blur();
33105             this.removeClass("x-tree-selected");
33106         }
33107     },
33108
33109     onMove : function(tree, node, oldParent, newParent, index, refNode){
33110         this.childIndent = null;
33111         if(this.rendered){
33112             var targetNode = newParent.ui.getContainer();
33113             if(!targetNode){//target not rendered
33114                 this.holder = document.createElement("div");
33115                 this.holder.appendChild(this.wrap);
33116                 return;
33117             }
33118             var insertBefore = refNode ? refNode.ui.getEl() : null;
33119             if(insertBefore){
33120                 targetNode.insertBefore(this.wrap, insertBefore);
33121             }else{
33122                 targetNode.appendChild(this.wrap);
33123             }
33124             this.node.renderIndent(true);
33125         }
33126     },
33127
33128     addClass : function(cls){
33129         if(this.elNode){
33130             Roo.fly(this.elNode).addClass(cls);
33131         }
33132     },
33133
33134     removeClass : function(cls){
33135         if(this.elNode){
33136             Roo.fly(this.elNode).removeClass(cls);
33137         }
33138     },
33139
33140     remove : function(){
33141         if(this.rendered){
33142             this.holder = document.createElement("div");
33143             this.holder.appendChild(this.wrap);
33144         }
33145     },
33146
33147     fireEvent : function(){
33148         return this.node.fireEvent.apply(this.node, arguments);
33149     },
33150
33151     initEvents : function(){
33152         this.node.on("move", this.onMove, this);
33153         var E = Roo.EventManager;
33154         var a = this.anchor;
33155
33156         var el = Roo.fly(a, '_treeui');
33157
33158         if(Roo.isOpera){ // opera render bug ignores the CSS
33159             el.setStyle("text-decoration", "none");
33160         }
33161
33162         el.on("click", this.onClick, this);
33163         el.on("dblclick", this.onDblClick, this);
33164
33165         if(this.checkbox){
33166             Roo.EventManager.on(this.checkbox,
33167                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33168         }
33169
33170         el.on("contextmenu", this.onContextMenu, this);
33171
33172         var icon = Roo.fly(this.iconNode);
33173         icon.on("click", this.onClick, this);
33174         icon.on("dblclick", this.onDblClick, this);
33175         icon.on("contextmenu", this.onContextMenu, this);
33176         E.on(this.ecNode, "click", this.ecClick, this, true);
33177
33178         if(this.node.disabled){
33179             this.addClass("x-tree-node-disabled");
33180         }
33181         if(this.node.hidden){
33182             this.addClass("x-tree-node-disabled");
33183         }
33184         var ot = this.node.getOwnerTree();
33185         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33186         if(dd && (!this.node.isRoot || ot.rootVisible)){
33187             Roo.dd.Registry.register(this.elNode, {
33188                 node: this.node,
33189                 handles: this.getDDHandles(),
33190                 isHandle: false
33191             });
33192         }
33193     },
33194
33195     getDDHandles : function(){
33196         return [this.iconNode, this.textNode];
33197     },
33198
33199     hide : function(){
33200         if(this.rendered){
33201             this.wrap.style.display = "none";
33202         }
33203     },
33204
33205     show : function(){
33206         if(this.rendered){
33207             this.wrap.style.display = "";
33208         }
33209     },
33210
33211     onContextMenu : function(e){
33212         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33213             e.preventDefault();
33214             this.focus();
33215             this.fireEvent("contextmenu", this.node, e);
33216         }
33217     },
33218
33219     onClick : function(e){
33220         if(this.dropping){
33221             e.stopEvent();
33222             return;
33223         }
33224         if(this.fireEvent("beforeclick", this.node, e) !== false){
33225             if(!this.disabled && this.node.attributes.href){
33226                 this.fireEvent("click", this.node, e);
33227                 return;
33228             }
33229             e.preventDefault();
33230             if(this.disabled){
33231                 return;
33232             }
33233
33234             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33235                 this.node.toggle();
33236             }
33237
33238             this.fireEvent("click", this.node, e);
33239         }else{
33240             e.stopEvent();
33241         }
33242     },
33243
33244     onDblClick : function(e){
33245         e.preventDefault();
33246         if(this.disabled){
33247             return;
33248         }
33249         if(this.checkbox){
33250             this.toggleCheck();
33251         }
33252         if(!this.animating && this.node.hasChildNodes()){
33253             this.node.toggle();
33254         }
33255         this.fireEvent("dblclick", this.node, e);
33256     },
33257
33258     onCheckChange : function(){
33259         var checked = this.checkbox.checked;
33260         this.node.attributes.checked = checked;
33261         this.fireEvent('checkchange', this.node, checked);
33262     },
33263
33264     ecClick : function(e){
33265         if(!this.animating && this.node.hasChildNodes()){
33266             this.node.toggle();
33267         }
33268     },
33269
33270     startDrop : function(){
33271         this.dropping = true;
33272     },
33273
33274     // delayed drop so the click event doesn't get fired on a drop
33275     endDrop : function(){
33276        setTimeout(function(){
33277            this.dropping = false;
33278        }.createDelegate(this), 50);
33279     },
33280
33281     expand : function(){
33282         this.updateExpandIcon();
33283         this.ctNode.style.display = "";
33284     },
33285
33286     focus : function(){
33287         if(!this.node.preventHScroll){
33288             try{this.anchor.focus();
33289             }catch(e){}
33290         }else if(!Roo.isIE){
33291             try{
33292                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33293                 var l = noscroll.scrollLeft;
33294                 this.anchor.focus();
33295                 noscroll.scrollLeft = l;
33296             }catch(e){}
33297         }
33298     },
33299
33300     toggleCheck : function(value){
33301         var cb = this.checkbox;
33302         if(cb){
33303             cb.checked = (value === undefined ? !cb.checked : value);
33304         }
33305     },
33306
33307     blur : function(){
33308         try{
33309             this.anchor.blur();
33310         }catch(e){}
33311     },
33312
33313     animExpand : function(callback){
33314         var ct = Roo.get(this.ctNode);
33315         ct.stopFx();
33316         if(!this.node.hasChildNodes()){
33317             this.updateExpandIcon();
33318             this.ctNode.style.display = "";
33319             Roo.callback(callback);
33320             return;
33321         }
33322         this.animating = true;
33323         this.updateExpandIcon();
33324
33325         ct.slideIn('t', {
33326            callback : function(){
33327                this.animating = false;
33328                Roo.callback(callback);
33329             },
33330             scope: this,
33331             duration: this.node.ownerTree.duration || .25
33332         });
33333     },
33334
33335     highlight : function(){
33336         var tree = this.node.getOwnerTree();
33337         Roo.fly(this.wrap).highlight(
33338             tree.hlColor || "C3DAF9",
33339             {endColor: tree.hlBaseColor}
33340         );
33341     },
33342
33343     collapse : function(){
33344         this.updateExpandIcon();
33345         this.ctNode.style.display = "none";
33346     },
33347
33348     animCollapse : function(callback){
33349         var ct = Roo.get(this.ctNode);
33350         ct.enableDisplayMode('block');
33351         ct.stopFx();
33352
33353         this.animating = true;
33354         this.updateExpandIcon();
33355
33356         ct.slideOut('t', {
33357             callback : function(){
33358                this.animating = false;
33359                Roo.callback(callback);
33360             },
33361             scope: this,
33362             duration: this.node.ownerTree.duration || .25
33363         });
33364     },
33365
33366     getContainer : function(){
33367         return this.ctNode;
33368     },
33369
33370     getEl : function(){
33371         return this.wrap;
33372     },
33373
33374     appendDDGhost : function(ghostNode){
33375         ghostNode.appendChild(this.elNode.cloneNode(true));
33376     },
33377
33378     getDDRepairXY : function(){
33379         return Roo.lib.Dom.getXY(this.iconNode);
33380     },
33381
33382     onRender : function(){
33383         this.render();
33384     },
33385
33386     render : function(bulkRender){
33387         var n = this.node, a = n.attributes;
33388         var targetNode = n.parentNode ?
33389               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33390
33391         if(!this.rendered){
33392             this.rendered = true;
33393
33394             this.renderElements(n, a, targetNode, bulkRender);
33395
33396             if(a.qtip){
33397                if(this.textNode.setAttributeNS){
33398                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33399                    if(a.qtipTitle){
33400                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33401                    }
33402                }else{
33403                    this.textNode.setAttribute("ext:qtip", a.qtip);
33404                    if(a.qtipTitle){
33405                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33406                    }
33407                }
33408             }else if(a.qtipCfg){
33409                 a.qtipCfg.target = Roo.id(this.textNode);
33410                 Roo.QuickTips.register(a.qtipCfg);
33411             }
33412             this.initEvents();
33413             if(!this.node.expanded){
33414                 this.updateExpandIcon();
33415             }
33416         }else{
33417             if(bulkRender === true) {
33418                 targetNode.appendChild(this.wrap);
33419             }
33420         }
33421     },
33422
33423     renderElements : function(n, a, targetNode, bulkRender)
33424     {
33425         // add some indent caching, this helps performance when rendering a large tree
33426         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33427         var t = n.getOwnerTree();
33428         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33429         if (typeof(n.attributes.html) != 'undefined') {
33430             txt = n.attributes.html;
33431         }
33432         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33433         var cb = typeof a.checked == 'boolean';
33434         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33435         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33436             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33437             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33438             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33439             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33440             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33441              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33442                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33443             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33444             "</li>"];
33445
33446         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33447             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33448                                 n.nextSibling.ui.getEl(), buf.join(""));
33449         }else{
33450             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33451         }
33452
33453         this.elNode = this.wrap.childNodes[0];
33454         this.ctNode = this.wrap.childNodes[1];
33455         var cs = this.elNode.childNodes;
33456         this.indentNode = cs[0];
33457         this.ecNode = cs[1];
33458         this.iconNode = cs[2];
33459         var index = 3;
33460         if(cb){
33461             this.checkbox = cs[3];
33462             index++;
33463         }
33464         this.anchor = cs[index];
33465         this.textNode = cs[index].firstChild;
33466     },
33467
33468     getAnchor : function(){
33469         return this.anchor;
33470     },
33471
33472     getTextEl : function(){
33473         return this.textNode;
33474     },
33475
33476     getIconEl : function(){
33477         return this.iconNode;
33478     },
33479
33480     isChecked : function(){
33481         return this.checkbox ? this.checkbox.checked : false;
33482     },
33483
33484     updateExpandIcon : function(){
33485         if(this.rendered){
33486             var n = this.node, c1, c2;
33487             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
33488             var hasChild = n.hasChildNodes();
33489             if(hasChild){
33490                 if(n.expanded){
33491                     cls += "-minus";
33492                     c1 = "x-tree-node-collapsed";
33493                     c2 = "x-tree-node-expanded";
33494                 }else{
33495                     cls += "-plus";
33496                     c1 = "x-tree-node-expanded";
33497                     c2 = "x-tree-node-collapsed";
33498                 }
33499                 if(this.wasLeaf){
33500                     this.removeClass("x-tree-node-leaf");
33501                     this.wasLeaf = false;
33502                 }
33503                 if(this.c1 != c1 || this.c2 != c2){
33504                     Roo.fly(this.elNode).replaceClass(c1, c2);
33505                     this.c1 = c1; this.c2 = c2;
33506                 }
33507             }else{
33508                 // this changes non-leafs into leafs if they have no children.
33509                 // it's not very rational behaviour..
33510                 
33511                 if(!this.wasLeaf && this.node.leaf){
33512                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
33513                     delete this.c1;
33514                     delete this.c2;
33515                     this.wasLeaf = true;
33516                 }
33517             }
33518             var ecc = "x-tree-ec-icon "+cls;
33519             if(this.ecc != ecc){
33520                 this.ecNode.className = ecc;
33521                 this.ecc = ecc;
33522             }
33523         }
33524     },
33525
33526     getChildIndent : function(){
33527         if(!this.childIndent){
33528             var buf = [];
33529             var p = this.node;
33530             while(p){
33531                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
33532                     if(!p.isLast()) {
33533                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
33534                     } else {
33535                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
33536                     }
33537                 }
33538                 p = p.parentNode;
33539             }
33540             this.childIndent = buf.join("");
33541         }
33542         return this.childIndent;
33543     },
33544
33545     renderIndent : function(){
33546         if(this.rendered){
33547             var indent = "";
33548             var p = this.node.parentNode;
33549             if(p){
33550                 indent = p.ui.getChildIndent();
33551             }
33552             if(this.indentMarkup != indent){ // don't rerender if not required
33553                 this.indentNode.innerHTML = indent;
33554                 this.indentMarkup = indent;
33555             }
33556             this.updateExpandIcon();
33557         }
33558     }
33559 };
33560
33561 Roo.tree.RootTreeNodeUI = function(){
33562     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
33563 };
33564 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
33565     render : function(){
33566         if(!this.rendered){
33567             var targetNode = this.node.ownerTree.innerCt.dom;
33568             this.node.expanded = true;
33569             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
33570             this.wrap = this.ctNode = targetNode.firstChild;
33571         }
33572     },
33573     collapse : function(){
33574     },
33575     expand : function(){
33576     }
33577 });/*
33578  * Based on:
33579  * Ext JS Library 1.1.1
33580  * Copyright(c) 2006-2007, Ext JS, LLC.
33581  *
33582  * Originally Released Under LGPL - original licence link has changed is not relivant.
33583  *
33584  * Fork - LGPL
33585  * <script type="text/javascript">
33586  */
33587 /**
33588  * @class Roo.tree.TreeLoader
33589  * @extends Roo.util.Observable
33590  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
33591  * nodes from a specified URL. The response must be a javascript Array definition
33592  * who's elements are node definition objects. eg:
33593  * <pre><code>
33594 {  success : true,
33595    data :      [
33596    
33597     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
33598     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
33599     ]
33600 }
33601
33602
33603 </code></pre>
33604  * <br><br>
33605  * The old style respose with just an array is still supported, but not recommended.
33606  * <br><br>
33607  *
33608  * A server request is sent, and child nodes are loaded only when a node is expanded.
33609  * The loading node's id is passed to the server under the parameter name "node" to
33610  * enable the server to produce the correct child nodes.
33611  * <br><br>
33612  * To pass extra parameters, an event handler may be attached to the "beforeload"
33613  * event, and the parameters specified in the TreeLoader's baseParams property:
33614  * <pre><code>
33615     myTreeLoader.on("beforeload", function(treeLoader, node) {
33616         this.baseParams.category = node.attributes.category;
33617     }, this);
33618 </code></pre><
33619  * This would pass an HTTP parameter called "category" to the server containing
33620  * the value of the Node's "category" attribute.
33621  * @constructor
33622  * Creates a new Treeloader.
33623  * @param {Object} config A config object containing config properties.
33624  */
33625 Roo.tree.TreeLoader = function(config){
33626     this.baseParams = {};
33627     this.requestMethod = "POST";
33628     Roo.apply(this, config);
33629
33630     this.addEvents({
33631     
33632         /**
33633          * @event beforeload
33634          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
33635          * @param {Object} This TreeLoader object.
33636          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33637          * @param {Object} callback The callback function specified in the {@link #load} call.
33638          */
33639         beforeload : true,
33640         /**
33641          * @event load
33642          * Fires when the node has been successfuly loaded.
33643          * @param {Object} This TreeLoader object.
33644          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33645          * @param {Object} response The response object containing the data from the server.
33646          */
33647         load : true,
33648         /**
33649          * @event loadexception
33650          * Fires if the network request failed.
33651          * @param {Object} This TreeLoader object.
33652          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33653          * @param {Object} response The response object containing the data from the server.
33654          */
33655         loadexception : true,
33656         /**
33657          * @event create
33658          * Fires before a node is created, enabling you to return custom Node types 
33659          * @param {Object} This TreeLoader object.
33660          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
33661          */
33662         create : true
33663     });
33664
33665     Roo.tree.TreeLoader.superclass.constructor.call(this);
33666 };
33667
33668 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
33669     /**
33670     * @cfg {String} dataUrl The URL from which to request a Json string which
33671     * specifies an array of node definition object representing the child nodes
33672     * to be loaded.
33673     */
33674     /**
33675     * @cfg {String} requestMethod either GET or POST
33676     * defaults to POST (due to BC)
33677     * to be loaded.
33678     */
33679     /**
33680     * @cfg {Object} baseParams (optional) An object containing properties which
33681     * specify HTTP parameters to be passed to each request for child nodes.
33682     */
33683     /**
33684     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
33685     * created by this loader. If the attributes sent by the server have an attribute in this object,
33686     * they take priority.
33687     */
33688     /**
33689     * @cfg {Object} uiProviders (optional) An object containing properties which
33690     * 
33691     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
33692     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
33693     * <i>uiProvider</i> attribute of a returned child node is a string rather
33694     * than a reference to a TreeNodeUI implementation, this that string value
33695     * is used as a property name in the uiProviders object. You can define the provider named
33696     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
33697     */
33698     uiProviders : {},
33699
33700     /**
33701     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
33702     * child nodes before loading.
33703     */
33704     clearOnLoad : true,
33705
33706     /**
33707     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
33708     * property on loading, rather than expecting an array. (eg. more compatible to a standard
33709     * Grid query { data : [ .....] }
33710     */
33711     
33712     root : false,
33713      /**
33714     * @cfg {String} queryParam (optional) 
33715     * Name of the query as it will be passed on the querystring (defaults to 'node')
33716     * eg. the request will be ?node=[id]
33717     */
33718     
33719     
33720     queryParam: false,
33721     
33722     /**
33723      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
33724      * This is called automatically when a node is expanded, but may be used to reload
33725      * a node (or append new children if the {@link #clearOnLoad} option is false.)
33726      * @param {Roo.tree.TreeNode} node
33727      * @param {Function} callback
33728      */
33729     load : function(node, callback){
33730         if(this.clearOnLoad){
33731             while(node.firstChild){
33732                 node.removeChild(node.firstChild);
33733             }
33734         }
33735         if(node.attributes.children){ // preloaded json children
33736             var cs = node.attributes.children;
33737             for(var i = 0, len = cs.length; i < len; i++){
33738                 node.appendChild(this.createNode(cs[i]));
33739             }
33740             if(typeof callback == "function"){
33741                 callback();
33742             }
33743         }else if(this.dataUrl){
33744             this.requestData(node, callback);
33745         }
33746     },
33747
33748     getParams: function(node){
33749         var buf = [], bp = this.baseParams;
33750         for(var key in bp){
33751             if(typeof bp[key] != "function"){
33752                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
33753             }
33754         }
33755         var n = this.queryParam === false ? 'node' : this.queryParam;
33756         buf.push(n + "=", encodeURIComponent(node.id));
33757         return buf.join("");
33758     },
33759
33760     requestData : function(node, callback){
33761         if(this.fireEvent("beforeload", this, node, callback) !== false){
33762             this.transId = Roo.Ajax.request({
33763                 method:this.requestMethod,
33764                 url: this.dataUrl||this.url,
33765                 success: this.handleResponse,
33766                 failure: this.handleFailure,
33767                 scope: this,
33768                 argument: {callback: callback, node: node},
33769                 params: this.getParams(node)
33770             });
33771         }else{
33772             // if the load is cancelled, make sure we notify
33773             // the node that we are done
33774             if(typeof callback == "function"){
33775                 callback();
33776             }
33777         }
33778     },
33779
33780     isLoading : function(){
33781         return this.transId ? true : false;
33782     },
33783
33784     abort : function(){
33785         if(this.isLoading()){
33786             Roo.Ajax.abort(this.transId);
33787         }
33788     },
33789
33790     // private
33791     createNode : function(attr)
33792     {
33793         // apply baseAttrs, nice idea Corey!
33794         if(this.baseAttrs){
33795             Roo.applyIf(attr, this.baseAttrs);
33796         }
33797         if(this.applyLoader !== false){
33798             attr.loader = this;
33799         }
33800         // uiProvider = depreciated..
33801         
33802         if(typeof(attr.uiProvider) == 'string'){
33803            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33804                 /**  eval:var:attr */ eval(attr.uiProvider);
33805         }
33806         if(typeof(this.uiProviders['default']) != 'undefined') {
33807             attr.uiProvider = this.uiProviders['default'];
33808         }
33809         
33810         this.fireEvent('create', this, attr);
33811         
33812         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33813         return(attr.leaf ?
33814                         new Roo.tree.TreeNode(attr) :
33815                         new Roo.tree.AsyncTreeNode(attr));
33816     },
33817
33818     processResponse : function(response, node, callback)
33819     {
33820         var json = response.responseText;
33821         try {
33822             
33823             var o = Roo.decode(json);
33824             
33825             if (this.root === false && typeof(o.success) != undefined) {
33826                 this.root = 'data'; // the default behaviour for list like data..
33827                 }
33828                 
33829             if (this.root !== false &&  !o.success) {
33830                 // it's a failure condition.
33831                 var a = response.argument;
33832                 this.fireEvent("loadexception", this, a.node, response);
33833                 Roo.log("Load failed - should have a handler really");
33834                 return;
33835             }
33836             
33837             
33838             
33839             if (this.root !== false) {
33840                  o = o[this.root];
33841             }
33842             
33843             for(var i = 0, len = o.length; i < len; i++){
33844                 var n = this.createNode(o[i]);
33845                 if(n){
33846                     node.appendChild(n);
33847                 }
33848             }
33849             if(typeof callback == "function"){
33850                 callback(this, node);
33851             }
33852         }catch(e){
33853             this.handleFailure(response);
33854         }
33855     },
33856
33857     handleResponse : function(response){
33858         this.transId = false;
33859         var a = response.argument;
33860         this.processResponse(response, a.node, a.callback);
33861         this.fireEvent("load", this, a.node, response);
33862     },
33863
33864     handleFailure : function(response)
33865     {
33866         // should handle failure better..
33867         this.transId = false;
33868         var a = response.argument;
33869         this.fireEvent("loadexception", this, a.node, response);
33870         if(typeof a.callback == "function"){
33871             a.callback(this, a.node);
33872         }
33873     }
33874 });/*
33875  * Based on:
33876  * Ext JS Library 1.1.1
33877  * Copyright(c) 2006-2007, Ext JS, LLC.
33878  *
33879  * Originally Released Under LGPL - original licence link has changed is not relivant.
33880  *
33881  * Fork - LGPL
33882  * <script type="text/javascript">
33883  */
33884
33885 /**
33886 * @class Roo.tree.TreeFilter
33887 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33888 * @param {TreePanel} tree
33889 * @param {Object} config (optional)
33890  */
33891 Roo.tree.TreeFilter = function(tree, config){
33892     this.tree = tree;
33893     this.filtered = {};
33894     Roo.apply(this, config);
33895 };
33896
33897 Roo.tree.TreeFilter.prototype = {
33898     clearBlank:false,
33899     reverse:false,
33900     autoClear:false,
33901     remove:false,
33902
33903      /**
33904      * Filter the data by a specific attribute.
33905      * @param {String/RegExp} value Either string that the attribute value
33906      * should start with or a RegExp to test against the attribute
33907      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33908      * @param {TreeNode} startNode (optional) The node to start the filter at.
33909      */
33910     filter : function(value, attr, startNode){
33911         attr = attr || "text";
33912         var f;
33913         if(typeof value == "string"){
33914             var vlen = value.length;
33915             // auto clear empty filter
33916             if(vlen == 0 && this.clearBlank){
33917                 this.clear();
33918                 return;
33919             }
33920             value = value.toLowerCase();
33921             f = function(n){
33922                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33923             };
33924         }else if(value.exec){ // regex?
33925             f = function(n){
33926                 return value.test(n.attributes[attr]);
33927             };
33928         }else{
33929             throw 'Illegal filter type, must be string or regex';
33930         }
33931         this.filterBy(f, null, startNode);
33932         },
33933
33934     /**
33935      * Filter by a function. The passed function will be called with each
33936      * node in the tree (or from the startNode). If the function returns true, the node is kept
33937      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33938      * @param {Function} fn The filter function
33939      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33940      */
33941     filterBy : function(fn, scope, startNode){
33942         startNode = startNode || this.tree.root;
33943         if(this.autoClear){
33944             this.clear();
33945         }
33946         var af = this.filtered, rv = this.reverse;
33947         var f = function(n){
33948             if(n == startNode){
33949                 return true;
33950             }
33951             if(af[n.id]){
33952                 return false;
33953             }
33954             var m = fn.call(scope || n, n);
33955             if(!m || rv){
33956                 af[n.id] = n;
33957                 n.ui.hide();
33958                 return false;
33959             }
33960             return true;
33961         };
33962         startNode.cascade(f);
33963         if(this.remove){
33964            for(var id in af){
33965                if(typeof id != "function"){
33966                    var n = af[id];
33967                    if(n && n.parentNode){
33968                        n.parentNode.removeChild(n);
33969                    }
33970                }
33971            }
33972         }
33973     },
33974
33975     /**
33976      * Clears the current filter. Note: with the "remove" option
33977      * set a filter cannot be cleared.
33978      */
33979     clear : function(){
33980         var t = this.tree;
33981         var af = this.filtered;
33982         for(var id in af){
33983             if(typeof id != "function"){
33984                 var n = af[id];
33985                 if(n){
33986                     n.ui.show();
33987                 }
33988             }
33989         }
33990         this.filtered = {};
33991     }
33992 };
33993 /*
33994  * Based on:
33995  * Ext JS Library 1.1.1
33996  * Copyright(c) 2006-2007, Ext JS, LLC.
33997  *
33998  * Originally Released Under LGPL - original licence link has changed is not relivant.
33999  *
34000  * Fork - LGPL
34001  * <script type="text/javascript">
34002  */
34003  
34004
34005 /**
34006  * @class Roo.tree.TreeSorter
34007  * Provides sorting of nodes in a TreePanel
34008  * 
34009  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34010  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34011  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34012  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34013  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34014  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34015  * @constructor
34016  * @param {TreePanel} tree
34017  * @param {Object} config
34018  */
34019 Roo.tree.TreeSorter = function(tree, config){
34020     Roo.apply(this, config);
34021     tree.on("beforechildrenrendered", this.doSort, this);
34022     tree.on("append", this.updateSort, this);
34023     tree.on("insert", this.updateSort, this);
34024     
34025     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34026     var p = this.property || "text";
34027     var sortType = this.sortType;
34028     var fs = this.folderSort;
34029     var cs = this.caseSensitive === true;
34030     var leafAttr = this.leafAttr || 'leaf';
34031
34032     this.sortFn = function(n1, n2){
34033         if(fs){
34034             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34035                 return 1;
34036             }
34037             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34038                 return -1;
34039             }
34040         }
34041         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34042         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34043         if(v1 < v2){
34044                         return dsc ? +1 : -1;
34045                 }else if(v1 > v2){
34046                         return dsc ? -1 : +1;
34047         }else{
34048                 return 0;
34049         }
34050     };
34051 };
34052
34053 Roo.tree.TreeSorter.prototype = {
34054     doSort : function(node){
34055         node.sort(this.sortFn);
34056     },
34057     
34058     compareNodes : function(n1, n2){
34059         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34060     },
34061     
34062     updateSort : function(tree, node){
34063         if(node.childrenRendered){
34064             this.doSort.defer(1, this, [node]);
34065         }
34066     }
34067 };/*
34068  * Based on:
34069  * Ext JS Library 1.1.1
34070  * Copyright(c) 2006-2007, Ext JS, LLC.
34071  *
34072  * Originally Released Under LGPL - original licence link has changed is not relivant.
34073  *
34074  * Fork - LGPL
34075  * <script type="text/javascript">
34076  */
34077
34078 if(Roo.dd.DropZone){
34079     
34080 Roo.tree.TreeDropZone = function(tree, config){
34081     this.allowParentInsert = false;
34082     this.allowContainerDrop = false;
34083     this.appendOnly = false;
34084     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34085     this.tree = tree;
34086     this.lastInsertClass = "x-tree-no-status";
34087     this.dragOverData = {};
34088 };
34089
34090 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34091     ddGroup : "TreeDD",
34092     scroll:  true,
34093     
34094     expandDelay : 1000,
34095     
34096     expandNode : function(node){
34097         if(node.hasChildNodes() && !node.isExpanded()){
34098             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34099         }
34100     },
34101     
34102     queueExpand : function(node){
34103         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34104     },
34105     
34106     cancelExpand : function(){
34107         if(this.expandProcId){
34108             clearTimeout(this.expandProcId);
34109             this.expandProcId = false;
34110         }
34111     },
34112     
34113     isValidDropPoint : function(n, pt, dd, e, data){
34114         if(!n || !data){ return false; }
34115         var targetNode = n.node;
34116         var dropNode = data.node;
34117         // default drop rules
34118         if(!(targetNode && targetNode.isTarget && pt)){
34119             return false;
34120         }
34121         if(pt == "append" && targetNode.allowChildren === false){
34122             return false;
34123         }
34124         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34125             return false;
34126         }
34127         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34128             return false;
34129         }
34130         // reuse the object
34131         var overEvent = this.dragOverData;
34132         overEvent.tree = this.tree;
34133         overEvent.target = targetNode;
34134         overEvent.data = data;
34135         overEvent.point = pt;
34136         overEvent.source = dd;
34137         overEvent.rawEvent = e;
34138         overEvent.dropNode = dropNode;
34139         overEvent.cancel = false;  
34140         var result = this.tree.fireEvent("nodedragover", overEvent);
34141         return overEvent.cancel === false && result !== false;
34142     },
34143     
34144     getDropPoint : function(e, n, dd)
34145     {
34146         var tn = n.node;
34147         if(tn.isRoot){
34148             return tn.allowChildren !== false ? "append" : false; // always append for root
34149         }
34150         var dragEl = n.ddel;
34151         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34152         var y = Roo.lib.Event.getPageY(e);
34153         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34154         
34155         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34156         var noAppend = tn.allowChildren === false;
34157         if(this.appendOnly || tn.parentNode.allowChildren === false){
34158             return noAppend ? false : "append";
34159         }
34160         var noBelow = false;
34161         if(!this.allowParentInsert){
34162             noBelow = tn.hasChildNodes() && tn.isExpanded();
34163         }
34164         var q = (b - t) / (noAppend ? 2 : 3);
34165         if(y >= t && y < (t + q)){
34166             return "above";
34167         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34168             return "below";
34169         }else{
34170             return "append";
34171         }
34172     },
34173     
34174     onNodeEnter : function(n, dd, e, data)
34175     {
34176         this.cancelExpand();
34177     },
34178     
34179     onNodeOver : function(n, dd, e, data)
34180     {
34181        
34182         var pt = this.getDropPoint(e, n, dd);
34183         var node = n.node;
34184         
34185         // auto node expand check
34186         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34187             this.queueExpand(node);
34188         }else if(pt != "append"){
34189             this.cancelExpand();
34190         }
34191         
34192         // set the insert point style on the target node
34193         var returnCls = this.dropNotAllowed;
34194         if(this.isValidDropPoint(n, pt, dd, e, data)){
34195            if(pt){
34196                var el = n.ddel;
34197                var cls;
34198                if(pt == "above"){
34199                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34200                    cls = "x-tree-drag-insert-above";
34201                }else if(pt == "below"){
34202                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34203                    cls = "x-tree-drag-insert-below";
34204                }else{
34205                    returnCls = "x-tree-drop-ok-append";
34206                    cls = "x-tree-drag-append";
34207                }
34208                if(this.lastInsertClass != cls){
34209                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34210                    this.lastInsertClass = cls;
34211                }
34212            }
34213        }
34214        return returnCls;
34215     },
34216     
34217     onNodeOut : function(n, dd, e, data){
34218         
34219         this.cancelExpand();
34220         this.removeDropIndicators(n);
34221     },
34222     
34223     onNodeDrop : function(n, dd, e, data){
34224         var point = this.getDropPoint(e, n, dd);
34225         var targetNode = n.node;
34226         targetNode.ui.startDrop();
34227         if(!this.isValidDropPoint(n, point, dd, e, data)){
34228             targetNode.ui.endDrop();
34229             return false;
34230         }
34231         // first try to find the drop node
34232         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34233         var dropEvent = {
34234             tree : this.tree,
34235             target: targetNode,
34236             data: data,
34237             point: point,
34238             source: dd,
34239             rawEvent: e,
34240             dropNode: dropNode,
34241             cancel: !dropNode   
34242         };
34243         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34244         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34245             targetNode.ui.endDrop();
34246             return false;
34247         }
34248         // allow target changing
34249         targetNode = dropEvent.target;
34250         if(point == "append" && !targetNode.isExpanded()){
34251             targetNode.expand(false, null, function(){
34252                 this.completeDrop(dropEvent);
34253             }.createDelegate(this));
34254         }else{
34255             this.completeDrop(dropEvent);
34256         }
34257         return true;
34258     },
34259     
34260     completeDrop : function(de){
34261         var ns = de.dropNode, p = de.point, t = de.target;
34262         if(!(ns instanceof Array)){
34263             ns = [ns];
34264         }
34265         var n;
34266         for(var i = 0, len = ns.length; i < len; i++){
34267             n = ns[i];
34268             if(p == "above"){
34269                 t.parentNode.insertBefore(n, t);
34270             }else if(p == "below"){
34271                 t.parentNode.insertBefore(n, t.nextSibling);
34272             }else{
34273                 t.appendChild(n);
34274             }
34275         }
34276         n.ui.focus();
34277         if(this.tree.hlDrop){
34278             n.ui.highlight();
34279         }
34280         t.ui.endDrop();
34281         this.tree.fireEvent("nodedrop", de);
34282     },
34283     
34284     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34285         if(this.tree.hlDrop){
34286             dropNode.ui.focus();
34287             dropNode.ui.highlight();
34288         }
34289         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34290     },
34291     
34292     getTree : function(){
34293         return this.tree;
34294     },
34295     
34296     removeDropIndicators : function(n){
34297         if(n && n.ddel){
34298             var el = n.ddel;
34299             Roo.fly(el).removeClass([
34300                     "x-tree-drag-insert-above",
34301                     "x-tree-drag-insert-below",
34302                     "x-tree-drag-append"]);
34303             this.lastInsertClass = "_noclass";
34304         }
34305     },
34306     
34307     beforeDragDrop : function(target, e, id){
34308         this.cancelExpand();
34309         return true;
34310     },
34311     
34312     afterRepair : function(data){
34313         if(data && Roo.enableFx){
34314             data.node.ui.highlight();
34315         }
34316         this.hideProxy();
34317     } 
34318     
34319 });
34320
34321 }
34322 /*
34323  * Based on:
34324  * Ext JS Library 1.1.1
34325  * Copyright(c) 2006-2007, Ext JS, LLC.
34326  *
34327  * Originally Released Under LGPL - original licence link has changed is not relivant.
34328  *
34329  * Fork - LGPL
34330  * <script type="text/javascript">
34331  */
34332  
34333
34334 if(Roo.dd.DragZone){
34335 Roo.tree.TreeDragZone = function(tree, config){
34336     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34337     this.tree = tree;
34338 };
34339
34340 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34341     ddGroup : "TreeDD",
34342    
34343     onBeforeDrag : function(data, e){
34344         var n = data.node;
34345         return n && n.draggable && !n.disabled;
34346     },
34347      
34348     
34349     onInitDrag : function(e){
34350         var data = this.dragData;
34351         this.tree.getSelectionModel().select(data.node);
34352         this.proxy.update("");
34353         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34354         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34355     },
34356     
34357     getRepairXY : function(e, data){
34358         return data.node.ui.getDDRepairXY();
34359     },
34360     
34361     onEndDrag : function(data, e){
34362         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34363         
34364         
34365     },
34366     
34367     onValidDrop : function(dd, e, id){
34368         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34369         this.hideProxy();
34370     },
34371     
34372     beforeInvalidDrop : function(e, id){
34373         // this scrolls the original position back into view
34374         var sm = this.tree.getSelectionModel();
34375         sm.clearSelections();
34376         sm.select(this.dragData.node);
34377     }
34378 });
34379 }/*
34380  * Based on:
34381  * Ext JS Library 1.1.1
34382  * Copyright(c) 2006-2007, Ext JS, LLC.
34383  *
34384  * Originally Released Under LGPL - original licence link has changed is not relivant.
34385  *
34386  * Fork - LGPL
34387  * <script type="text/javascript">
34388  */
34389 /**
34390  * @class Roo.tree.TreeEditor
34391  * @extends Roo.Editor
34392  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34393  * as the editor field.
34394  * @constructor
34395  * @param {Object} config (used to be the tree panel.)
34396  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34397  * 
34398  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34399  * @cfg {Roo.form.TextField|Object} field The field configuration
34400  *
34401  * 
34402  */
34403 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34404     var tree = config;
34405     var field;
34406     if (oldconfig) { // old style..
34407         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34408     } else {
34409         // new style..
34410         tree = config.tree;
34411         config.field = config.field  || {};
34412         config.field.xtype = 'TextField';
34413         field = Roo.factory(config.field, Roo.form);
34414     }
34415     config = config || {};
34416     
34417     
34418     this.addEvents({
34419         /**
34420          * @event beforenodeedit
34421          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34422          * false from the handler of this event.
34423          * @param {Editor} this
34424          * @param {Roo.tree.Node} node 
34425          */
34426         "beforenodeedit" : true
34427     });
34428     
34429     //Roo.log(config);
34430     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34431
34432     this.tree = tree;
34433
34434     tree.on('beforeclick', this.beforeNodeClick, this);
34435     tree.getTreeEl().on('mousedown', this.hide, this);
34436     this.on('complete', this.updateNode, this);
34437     this.on('beforestartedit', this.fitToTree, this);
34438     this.on('startedit', this.bindScroll, this, {delay:10});
34439     this.on('specialkey', this.onSpecialKey, this);
34440 };
34441
34442 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34443     /**
34444      * @cfg {String} alignment
34445      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34446      */
34447     alignment: "l-l",
34448     // inherit
34449     autoSize: false,
34450     /**
34451      * @cfg {Boolean} hideEl
34452      * True to hide the bound element while the editor is displayed (defaults to false)
34453      */
34454     hideEl : false,
34455     /**
34456      * @cfg {String} cls
34457      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34458      */
34459     cls: "x-small-editor x-tree-editor",
34460     /**
34461      * @cfg {Boolean} shim
34462      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34463      */
34464     shim:false,
34465     // inherit
34466     shadow:"frame",
34467     /**
34468      * @cfg {Number} maxWidth
34469      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34470      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34471      * scroll and client offsets into account prior to each edit.
34472      */
34473     maxWidth: 250,
34474
34475     editDelay : 350,
34476
34477     // private
34478     fitToTree : function(ed, el){
34479         var td = this.tree.getTreeEl().dom, nd = el.dom;
34480         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34481             td.scrollLeft = nd.offsetLeft;
34482         }
34483         var w = Math.min(
34484                 this.maxWidth,
34485                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
34486         this.setSize(w, '');
34487         
34488         return this.fireEvent('beforenodeedit', this, this.editNode);
34489         
34490     },
34491
34492     // private
34493     triggerEdit : function(node){
34494         this.completeEdit();
34495         this.editNode = node;
34496         this.startEdit(node.ui.textNode, node.text);
34497     },
34498
34499     // private
34500     bindScroll : function(){
34501         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
34502     },
34503
34504     // private
34505     beforeNodeClick : function(node, e){
34506         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
34507         this.lastClick = new Date();
34508         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
34509             e.stopEvent();
34510             this.triggerEdit(node);
34511             return false;
34512         }
34513         return true;
34514     },
34515
34516     // private
34517     updateNode : function(ed, value){
34518         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
34519         this.editNode.setText(value);
34520     },
34521
34522     // private
34523     onHide : function(){
34524         Roo.tree.TreeEditor.superclass.onHide.call(this);
34525         if(this.editNode){
34526             this.editNode.ui.focus();
34527         }
34528     },
34529
34530     // private
34531     onSpecialKey : function(field, e){
34532         var k = e.getKey();
34533         if(k == e.ESC){
34534             e.stopEvent();
34535             this.cancelEdit();
34536         }else if(k == e.ENTER && !e.hasModifier()){
34537             e.stopEvent();
34538             this.completeEdit();
34539         }
34540     }
34541 });//<Script type="text/javascript">
34542 /*
34543  * Based on:
34544  * Ext JS Library 1.1.1
34545  * Copyright(c) 2006-2007, Ext JS, LLC.
34546  *
34547  * Originally Released Under LGPL - original licence link has changed is not relivant.
34548  *
34549  * Fork - LGPL
34550  * <script type="text/javascript">
34551  */
34552  
34553 /**
34554  * Not documented??? - probably should be...
34555  */
34556
34557 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
34558     //focus: Roo.emptyFn, // prevent odd scrolling behavior
34559     
34560     renderElements : function(n, a, targetNode, bulkRender){
34561         //consel.log("renderElements?");
34562         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34563
34564         var t = n.getOwnerTree();
34565         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
34566         
34567         var cols = t.columns;
34568         var bw = t.borderWidth;
34569         var c = cols[0];
34570         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34571          var cb = typeof a.checked == "boolean";
34572         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34573         var colcls = 'x-t-' + tid + '-c0';
34574         var buf = [
34575             '<li class="x-tree-node">',
34576             
34577                 
34578                 '<div class="x-tree-node-el ', a.cls,'">',
34579                     // extran...
34580                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
34581                 
34582                 
34583                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
34584                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
34585                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
34586                            (a.icon ? ' x-tree-node-inline-icon' : ''),
34587                            (a.iconCls ? ' '+a.iconCls : ''),
34588                            '" unselectable="on" />',
34589                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
34590                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
34591                              
34592                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34593                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
34594                             '<span unselectable="on" qtip="' + tx + '">',
34595                              tx,
34596                              '</span></a>' ,
34597                     '</div>',
34598                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34599                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
34600                  ];
34601         for(var i = 1, len = cols.length; i < len; i++){
34602             c = cols[i];
34603             colcls = 'x-t-' + tid + '-c' +i;
34604             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34605             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
34606                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
34607                       "</div>");
34608          }
34609          
34610          buf.push(
34611             '</a>',
34612             '<div class="x-clear"></div></div>',
34613             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34614             "</li>");
34615         
34616         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34617             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34618                                 n.nextSibling.ui.getEl(), buf.join(""));
34619         }else{
34620             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34621         }
34622         var el = this.wrap.firstChild;
34623         this.elRow = el;
34624         this.elNode = el.firstChild;
34625         this.ranchor = el.childNodes[1];
34626         this.ctNode = this.wrap.childNodes[1];
34627         var cs = el.firstChild.childNodes;
34628         this.indentNode = cs[0];
34629         this.ecNode = cs[1];
34630         this.iconNode = cs[2];
34631         var index = 3;
34632         if(cb){
34633             this.checkbox = cs[3];
34634             index++;
34635         }
34636         this.anchor = cs[index];
34637         
34638         this.textNode = cs[index].firstChild;
34639         
34640         //el.on("click", this.onClick, this);
34641         //el.on("dblclick", this.onDblClick, this);
34642         
34643         
34644        // console.log(this);
34645     },
34646     initEvents : function(){
34647         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
34648         
34649             
34650         var a = this.ranchor;
34651
34652         var el = Roo.get(a);
34653
34654         if(Roo.isOpera){ // opera render bug ignores the CSS
34655             el.setStyle("text-decoration", "none");
34656         }
34657
34658         el.on("click", this.onClick, this);
34659         el.on("dblclick", this.onDblClick, this);
34660         el.on("contextmenu", this.onContextMenu, this);
34661         
34662     },
34663     
34664     /*onSelectedChange : function(state){
34665         if(state){
34666             this.focus();
34667             this.addClass("x-tree-selected");
34668         }else{
34669             //this.blur();
34670             this.removeClass("x-tree-selected");
34671         }
34672     },*/
34673     addClass : function(cls){
34674         if(this.elRow){
34675             Roo.fly(this.elRow).addClass(cls);
34676         }
34677         
34678     },
34679     
34680     
34681     removeClass : function(cls){
34682         if(this.elRow){
34683             Roo.fly(this.elRow).removeClass(cls);
34684         }
34685     }
34686
34687     
34688     
34689 });//<Script type="text/javascript">
34690
34691 /*
34692  * Based on:
34693  * Ext JS Library 1.1.1
34694  * Copyright(c) 2006-2007, Ext JS, LLC.
34695  *
34696  * Originally Released Under LGPL - original licence link has changed is not relivant.
34697  *
34698  * Fork - LGPL
34699  * <script type="text/javascript">
34700  */
34701  
34702
34703 /**
34704  * @class Roo.tree.ColumnTree
34705  * @extends Roo.data.TreePanel
34706  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
34707  * @cfg {int} borderWidth  compined right/left border allowance
34708  * @constructor
34709  * @param {String/HTMLElement/Element} el The container element
34710  * @param {Object} config
34711  */
34712 Roo.tree.ColumnTree =  function(el, config)
34713 {
34714    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
34715    this.addEvents({
34716         /**
34717         * @event resize
34718         * Fire this event on a container when it resizes
34719         * @param {int} w Width
34720         * @param {int} h Height
34721         */
34722        "resize" : true
34723     });
34724     this.on('resize', this.onResize, this);
34725 };
34726
34727 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
34728     //lines:false,
34729     
34730     
34731     borderWidth: Roo.isBorderBox ? 0 : 2, 
34732     headEls : false,
34733     
34734     render : function(){
34735         // add the header.....
34736        
34737         Roo.tree.ColumnTree.superclass.render.apply(this);
34738         
34739         this.el.addClass('x-column-tree');
34740         
34741         this.headers = this.el.createChild(
34742             {cls:'x-tree-headers'},this.innerCt.dom);
34743    
34744         var cols = this.columns, c;
34745         var totalWidth = 0;
34746         this.headEls = [];
34747         var  len = cols.length;
34748         for(var i = 0; i < len; i++){
34749              c = cols[i];
34750              totalWidth += c.width;
34751             this.headEls.push(this.headers.createChild({
34752                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
34753                  cn: {
34754                      cls:'x-tree-hd-text',
34755                      html: c.header
34756                  },
34757                  style:'width:'+(c.width-this.borderWidth)+'px;'
34758              }));
34759         }
34760         this.headers.createChild({cls:'x-clear'});
34761         // prevent floats from wrapping when clipped
34762         this.headers.setWidth(totalWidth);
34763         //this.innerCt.setWidth(totalWidth);
34764         this.innerCt.setStyle({ overflow: 'auto' });
34765         this.onResize(this.width, this.height);
34766              
34767         
34768     },
34769     onResize : function(w,h)
34770     {
34771         this.height = h;
34772         this.width = w;
34773         // resize cols..
34774         this.innerCt.setWidth(this.width);
34775         this.innerCt.setHeight(this.height-20);
34776         
34777         // headers...
34778         var cols = this.columns, c;
34779         var totalWidth = 0;
34780         var expEl = false;
34781         var len = cols.length;
34782         for(var i = 0; i < len; i++){
34783             c = cols[i];
34784             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34785                 // it's the expander..
34786                 expEl  = this.headEls[i];
34787                 continue;
34788             }
34789             totalWidth += c.width;
34790             
34791         }
34792         if (expEl) {
34793             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34794         }
34795         this.headers.setWidth(w-20);
34796
34797         
34798         
34799         
34800     }
34801 });
34802 /*
34803  * Based on:
34804  * Ext JS Library 1.1.1
34805  * Copyright(c) 2006-2007, Ext JS, LLC.
34806  *
34807  * Originally Released Under LGPL - original licence link has changed is not relivant.
34808  *
34809  * Fork - LGPL
34810  * <script type="text/javascript">
34811  */
34812  
34813 /**
34814  * @class Roo.menu.Menu
34815  * @extends Roo.util.Observable
34816  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34817  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34818  * @constructor
34819  * Creates a new Menu
34820  * @param {Object} config Configuration options
34821  */
34822 Roo.menu.Menu = function(config){
34823     Roo.apply(this, config);
34824     this.id = this.id || Roo.id();
34825     this.addEvents({
34826         /**
34827          * @event beforeshow
34828          * Fires before this menu is displayed
34829          * @param {Roo.menu.Menu} this
34830          */
34831         beforeshow : true,
34832         /**
34833          * @event beforehide
34834          * Fires before this menu is hidden
34835          * @param {Roo.menu.Menu} this
34836          */
34837         beforehide : true,
34838         /**
34839          * @event show
34840          * Fires after this menu is displayed
34841          * @param {Roo.menu.Menu} this
34842          */
34843         show : true,
34844         /**
34845          * @event hide
34846          * Fires after this menu is hidden
34847          * @param {Roo.menu.Menu} this
34848          */
34849         hide : true,
34850         /**
34851          * @event click
34852          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34853          * @param {Roo.menu.Menu} this
34854          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34855          * @param {Roo.EventObject} e
34856          */
34857         click : true,
34858         /**
34859          * @event mouseover
34860          * Fires when the mouse is hovering over this menu
34861          * @param {Roo.menu.Menu} this
34862          * @param {Roo.EventObject} e
34863          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34864          */
34865         mouseover : true,
34866         /**
34867          * @event mouseout
34868          * Fires when the mouse exits this menu
34869          * @param {Roo.menu.Menu} this
34870          * @param {Roo.EventObject} e
34871          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34872          */
34873         mouseout : true,
34874         /**
34875          * @event itemclick
34876          * Fires when a menu item contained in this menu is clicked
34877          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34878          * @param {Roo.EventObject} e
34879          */
34880         itemclick: true
34881     });
34882     if (this.registerMenu) {
34883         Roo.menu.MenuMgr.register(this);
34884     }
34885     
34886     var mis = this.items;
34887     this.items = new Roo.util.MixedCollection();
34888     if(mis){
34889         this.add.apply(this, mis);
34890     }
34891 };
34892
34893 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34894     /**
34895      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34896      */
34897     minWidth : 120,
34898     /**
34899      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34900      * for bottom-right shadow (defaults to "sides")
34901      */
34902     shadow : "sides",
34903     /**
34904      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34905      * this menu (defaults to "tl-tr?")
34906      */
34907     subMenuAlign : "tl-tr?",
34908     /**
34909      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34910      * relative to its element of origin (defaults to "tl-bl?")
34911      */
34912     defaultAlign : "tl-bl?",
34913     /**
34914      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34915      */
34916     allowOtherMenus : false,
34917     /**
34918      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34919      */
34920     registerMenu : true,
34921
34922     hidden:true,
34923
34924     // private
34925     render : function(){
34926         if(this.el){
34927             return;
34928         }
34929         var el = this.el = new Roo.Layer({
34930             cls: "x-menu",
34931             shadow:this.shadow,
34932             constrain: false,
34933             parentEl: this.parentEl || document.body,
34934             zindex:15000
34935         });
34936
34937         this.keyNav = new Roo.menu.MenuNav(this);
34938
34939         if(this.plain){
34940             el.addClass("x-menu-plain");
34941         }
34942         if(this.cls){
34943             el.addClass(this.cls);
34944         }
34945         // generic focus element
34946         this.focusEl = el.createChild({
34947             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34948         });
34949         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34950         ul.on("click", this.onClick, this);
34951         ul.on("mouseover", this.onMouseOver, this);
34952         ul.on("mouseout", this.onMouseOut, this);
34953         this.items.each(function(item){
34954             if (item.hidden) {
34955                 return;
34956             }
34957             
34958             var li = document.createElement("li");
34959             li.className = "x-menu-list-item";
34960             ul.dom.appendChild(li);
34961             item.render(li, this);
34962         }, this);
34963         this.ul = ul;
34964         this.autoWidth();
34965     },
34966
34967     // private
34968     autoWidth : function(){
34969         var el = this.el, ul = this.ul;
34970         if(!el){
34971             return;
34972         }
34973         var w = this.width;
34974         if(w){
34975             el.setWidth(w);
34976         }else if(Roo.isIE){
34977             el.setWidth(this.minWidth);
34978             var t = el.dom.offsetWidth; // force recalc
34979             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34980         }
34981     },
34982
34983     // private
34984     delayAutoWidth : function(){
34985         if(this.rendered){
34986             if(!this.awTask){
34987                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34988             }
34989             this.awTask.delay(20);
34990         }
34991     },
34992
34993     // private
34994     findTargetItem : function(e){
34995         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34996         if(t && t.menuItemId){
34997             return this.items.get(t.menuItemId);
34998         }
34999     },
35000
35001     // private
35002     onClick : function(e){
35003         var t;
35004         if(t = this.findTargetItem(e)){
35005             t.onClick(e);
35006             this.fireEvent("click", this, t, e);
35007         }
35008     },
35009
35010     // private
35011     setActiveItem : function(item, autoExpand){
35012         if(item != this.activeItem){
35013             if(this.activeItem){
35014                 this.activeItem.deactivate();
35015             }
35016             this.activeItem = item;
35017             item.activate(autoExpand);
35018         }else if(autoExpand){
35019             item.expandMenu();
35020         }
35021     },
35022
35023     // private
35024     tryActivate : function(start, step){
35025         var items = this.items;
35026         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35027             var item = items.get(i);
35028             if(!item.disabled && item.canActivate){
35029                 this.setActiveItem(item, false);
35030                 return item;
35031             }
35032         }
35033         return false;
35034     },
35035
35036     // private
35037     onMouseOver : function(e){
35038         var t;
35039         if(t = this.findTargetItem(e)){
35040             if(t.canActivate && !t.disabled){
35041                 this.setActiveItem(t, true);
35042             }
35043         }
35044         this.fireEvent("mouseover", this, e, t);
35045     },
35046
35047     // private
35048     onMouseOut : function(e){
35049         var t;
35050         if(t = this.findTargetItem(e)){
35051             if(t == this.activeItem && t.shouldDeactivate(e)){
35052                 this.activeItem.deactivate();
35053                 delete this.activeItem;
35054             }
35055         }
35056         this.fireEvent("mouseout", this, e, t);
35057     },
35058
35059     /**
35060      * Read-only.  Returns true if the menu is currently displayed, else false.
35061      * @type Boolean
35062      */
35063     isVisible : function(){
35064         return this.el && !this.hidden;
35065     },
35066
35067     /**
35068      * Displays this menu relative to another element
35069      * @param {String/HTMLElement/Roo.Element} element The element to align to
35070      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35071      * the element (defaults to this.defaultAlign)
35072      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35073      */
35074     show : function(el, pos, parentMenu){
35075         this.parentMenu = parentMenu;
35076         if(!this.el){
35077             this.render();
35078         }
35079         this.fireEvent("beforeshow", this);
35080         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35081     },
35082
35083     /**
35084      * Displays this menu at a specific xy position
35085      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35086      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35087      */
35088     showAt : function(xy, parentMenu, /* private: */_e){
35089         this.parentMenu = parentMenu;
35090         if(!this.el){
35091             this.render();
35092         }
35093         if(_e !== false){
35094             this.fireEvent("beforeshow", this);
35095             xy = this.el.adjustForConstraints(xy);
35096         }
35097         this.el.setXY(xy);
35098         this.el.show();
35099         this.hidden = false;
35100         this.focus();
35101         this.fireEvent("show", this);
35102     },
35103
35104     focus : function(){
35105         if(!this.hidden){
35106             this.doFocus.defer(50, this);
35107         }
35108     },
35109
35110     doFocus : function(){
35111         if(!this.hidden){
35112             this.focusEl.focus();
35113         }
35114     },
35115
35116     /**
35117      * Hides this menu and optionally all parent menus
35118      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35119      */
35120     hide : function(deep){
35121         if(this.el && this.isVisible()){
35122             this.fireEvent("beforehide", this);
35123             if(this.activeItem){
35124                 this.activeItem.deactivate();
35125                 this.activeItem = null;
35126             }
35127             this.el.hide();
35128             this.hidden = true;
35129             this.fireEvent("hide", this);
35130         }
35131         if(deep === true && this.parentMenu){
35132             this.parentMenu.hide(true);
35133         }
35134     },
35135
35136     /**
35137      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35138      * Any of the following are valid:
35139      * <ul>
35140      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35141      * <li>An HTMLElement object which will be converted to a menu item</li>
35142      * <li>A menu item config object that will be created as a new menu item</li>
35143      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35144      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35145      * </ul>
35146      * Usage:
35147      * <pre><code>
35148 // Create the menu
35149 var menu = new Roo.menu.Menu();
35150
35151 // Create a menu item to add by reference
35152 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35153
35154 // Add a bunch of items at once using different methods.
35155 // Only the last item added will be returned.
35156 var item = menu.add(
35157     menuItem,                // add existing item by ref
35158     'Dynamic Item',          // new TextItem
35159     '-',                     // new separator
35160     { text: 'Config Item' }  // new item by config
35161 );
35162 </code></pre>
35163      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35164      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35165      */
35166     add : function(){
35167         var a = arguments, l = a.length, item;
35168         for(var i = 0; i < l; i++){
35169             var el = a[i];
35170             if ((typeof(el) == "object") && el.xtype && el.xns) {
35171                 el = Roo.factory(el, Roo.menu);
35172             }
35173             
35174             if(el.render){ // some kind of Item
35175                 item = this.addItem(el);
35176             }else if(typeof el == "string"){ // string
35177                 if(el == "separator" || el == "-"){
35178                     item = this.addSeparator();
35179                 }else{
35180                     item = this.addText(el);
35181                 }
35182             }else if(el.tagName || el.el){ // element
35183                 item = this.addElement(el);
35184             }else if(typeof el == "object"){ // must be menu item config?
35185                 item = this.addMenuItem(el);
35186             }
35187         }
35188         return item;
35189     },
35190
35191     /**
35192      * Returns this menu's underlying {@link Roo.Element} object
35193      * @return {Roo.Element} The element
35194      */
35195     getEl : function(){
35196         if(!this.el){
35197             this.render();
35198         }
35199         return this.el;
35200     },
35201
35202     /**
35203      * Adds a separator bar to the menu
35204      * @return {Roo.menu.Item} The menu item that was added
35205      */
35206     addSeparator : function(){
35207         return this.addItem(new Roo.menu.Separator());
35208     },
35209
35210     /**
35211      * Adds an {@link Roo.Element} object to the menu
35212      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35213      * @return {Roo.menu.Item} The menu item that was added
35214      */
35215     addElement : function(el){
35216         return this.addItem(new Roo.menu.BaseItem(el));
35217     },
35218
35219     /**
35220      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35221      * @param {Roo.menu.Item} item The menu item to add
35222      * @return {Roo.menu.Item} The menu item that was added
35223      */
35224     addItem : function(item){
35225         this.items.add(item);
35226         if(this.ul){
35227             var li = document.createElement("li");
35228             li.className = "x-menu-list-item";
35229             this.ul.dom.appendChild(li);
35230             item.render(li, this);
35231             this.delayAutoWidth();
35232         }
35233         return item;
35234     },
35235
35236     /**
35237      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35238      * @param {Object} config A MenuItem config object
35239      * @return {Roo.menu.Item} The menu item that was added
35240      */
35241     addMenuItem : function(config){
35242         if(!(config instanceof Roo.menu.Item)){
35243             if(typeof config.checked == "boolean"){ // must be check menu item config?
35244                 config = new Roo.menu.CheckItem(config);
35245             }else{
35246                 config = new Roo.menu.Item(config);
35247             }
35248         }
35249         return this.addItem(config);
35250     },
35251
35252     /**
35253      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35254      * @param {String} text The text to display in the menu item
35255      * @return {Roo.menu.Item} The menu item that was added
35256      */
35257     addText : function(text){
35258         return this.addItem(new Roo.menu.TextItem({ text : text }));
35259     },
35260
35261     /**
35262      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35263      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35264      * @param {Roo.menu.Item} item The menu item to add
35265      * @return {Roo.menu.Item} The menu item that was added
35266      */
35267     insert : function(index, item){
35268         this.items.insert(index, item);
35269         if(this.ul){
35270             var li = document.createElement("li");
35271             li.className = "x-menu-list-item";
35272             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35273             item.render(li, this);
35274             this.delayAutoWidth();
35275         }
35276         return item;
35277     },
35278
35279     /**
35280      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35281      * @param {Roo.menu.Item} item The menu item to remove
35282      */
35283     remove : function(item){
35284         this.items.removeKey(item.id);
35285         item.destroy();
35286     },
35287
35288     /**
35289      * Removes and destroys all items in the menu
35290      */
35291     removeAll : function(){
35292         var f;
35293         while(f = this.items.first()){
35294             this.remove(f);
35295         }
35296     }
35297 });
35298
35299 // MenuNav is a private utility class used internally by the Menu
35300 Roo.menu.MenuNav = function(menu){
35301     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35302     this.scope = this.menu = menu;
35303 };
35304
35305 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35306     doRelay : function(e, h){
35307         var k = e.getKey();
35308         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35309             this.menu.tryActivate(0, 1);
35310             return false;
35311         }
35312         return h.call(this.scope || this, e, this.menu);
35313     },
35314
35315     up : function(e, m){
35316         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35317             m.tryActivate(m.items.length-1, -1);
35318         }
35319     },
35320
35321     down : function(e, m){
35322         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35323             m.tryActivate(0, 1);
35324         }
35325     },
35326
35327     right : function(e, m){
35328         if(m.activeItem){
35329             m.activeItem.expandMenu(true);
35330         }
35331     },
35332
35333     left : function(e, m){
35334         m.hide();
35335         if(m.parentMenu && m.parentMenu.activeItem){
35336             m.parentMenu.activeItem.activate();
35337         }
35338     },
35339
35340     enter : function(e, m){
35341         if(m.activeItem){
35342             e.stopPropagation();
35343             m.activeItem.onClick(e);
35344             m.fireEvent("click", this, m.activeItem);
35345             return true;
35346         }
35347     }
35348 });/*
35349  * Based on:
35350  * Ext JS Library 1.1.1
35351  * Copyright(c) 2006-2007, Ext JS, LLC.
35352  *
35353  * Originally Released Under LGPL - original licence link has changed is not relivant.
35354  *
35355  * Fork - LGPL
35356  * <script type="text/javascript">
35357  */
35358  
35359 /**
35360  * @class Roo.menu.MenuMgr
35361  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35362  * @singleton
35363  */
35364 Roo.menu.MenuMgr = function(){
35365    var menus, active, groups = {}, attached = false, lastShow = new Date();
35366
35367    // private - called when first menu is created
35368    function init(){
35369        menus = {};
35370        active = new Roo.util.MixedCollection();
35371        Roo.get(document).addKeyListener(27, function(){
35372            if(active.length > 0){
35373                hideAll();
35374            }
35375        });
35376    }
35377
35378    // private
35379    function hideAll(){
35380        if(active && active.length > 0){
35381            var c = active.clone();
35382            c.each(function(m){
35383                m.hide();
35384            });
35385        }
35386    }
35387
35388    // private
35389    function onHide(m){
35390        active.remove(m);
35391        if(active.length < 1){
35392            Roo.get(document).un("mousedown", onMouseDown);
35393            attached = false;
35394        }
35395    }
35396
35397    // private
35398    function onShow(m){
35399        var last = active.last();
35400        lastShow = new Date();
35401        active.add(m);
35402        if(!attached){
35403            Roo.get(document).on("mousedown", onMouseDown);
35404            attached = true;
35405        }
35406        if(m.parentMenu){
35407           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35408           m.parentMenu.activeChild = m;
35409        }else if(last && last.isVisible()){
35410           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35411        }
35412    }
35413
35414    // private
35415    function onBeforeHide(m){
35416        if(m.activeChild){
35417            m.activeChild.hide();
35418        }
35419        if(m.autoHideTimer){
35420            clearTimeout(m.autoHideTimer);
35421            delete m.autoHideTimer;
35422        }
35423    }
35424
35425    // private
35426    function onBeforeShow(m){
35427        var pm = m.parentMenu;
35428        if(!pm && !m.allowOtherMenus){
35429            hideAll();
35430        }else if(pm && pm.activeChild && active != m){
35431            pm.activeChild.hide();
35432        }
35433    }
35434
35435    // private
35436    function onMouseDown(e){
35437        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35438            hideAll();
35439        }
35440    }
35441
35442    // private
35443    function onBeforeCheck(mi, state){
35444        if(state){
35445            var g = groups[mi.group];
35446            for(var i = 0, l = g.length; i < l; i++){
35447                if(g[i] != mi){
35448                    g[i].setChecked(false);
35449                }
35450            }
35451        }
35452    }
35453
35454    return {
35455
35456        /**
35457         * Hides all menus that are currently visible
35458         */
35459        hideAll : function(){
35460             hideAll();  
35461        },
35462
35463        // private
35464        register : function(menu){
35465            if(!menus){
35466                init();
35467            }
35468            menus[menu.id] = menu;
35469            menu.on("beforehide", onBeforeHide);
35470            menu.on("hide", onHide);
35471            menu.on("beforeshow", onBeforeShow);
35472            menu.on("show", onShow);
35473            var g = menu.group;
35474            if(g && menu.events["checkchange"]){
35475                if(!groups[g]){
35476                    groups[g] = [];
35477                }
35478                groups[g].push(menu);
35479                menu.on("checkchange", onCheck);
35480            }
35481        },
35482
35483         /**
35484          * Returns a {@link Roo.menu.Menu} object
35485          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
35486          * be used to generate and return a new Menu instance.
35487          */
35488        get : function(menu){
35489            if(typeof menu == "string"){ // menu id
35490                return menus[menu];
35491            }else if(menu.events){  // menu instance
35492                return menu;
35493            }else if(typeof menu.length == 'number'){ // array of menu items?
35494                return new Roo.menu.Menu({items:menu});
35495            }else{ // otherwise, must be a config
35496                return new Roo.menu.Menu(menu);
35497            }
35498        },
35499
35500        // private
35501        unregister : function(menu){
35502            delete menus[menu.id];
35503            menu.un("beforehide", onBeforeHide);
35504            menu.un("hide", onHide);
35505            menu.un("beforeshow", onBeforeShow);
35506            menu.un("show", onShow);
35507            var g = menu.group;
35508            if(g && menu.events["checkchange"]){
35509                groups[g].remove(menu);
35510                menu.un("checkchange", onCheck);
35511            }
35512        },
35513
35514        // private
35515        registerCheckable : function(menuItem){
35516            var g = menuItem.group;
35517            if(g){
35518                if(!groups[g]){
35519                    groups[g] = [];
35520                }
35521                groups[g].push(menuItem);
35522                menuItem.on("beforecheckchange", onBeforeCheck);
35523            }
35524        },
35525
35526        // private
35527        unregisterCheckable : function(menuItem){
35528            var g = menuItem.group;
35529            if(g){
35530                groups[g].remove(menuItem);
35531                menuItem.un("beforecheckchange", onBeforeCheck);
35532            }
35533        }
35534    };
35535 }();/*
35536  * Based on:
35537  * Ext JS Library 1.1.1
35538  * Copyright(c) 2006-2007, Ext JS, LLC.
35539  *
35540  * Originally Released Under LGPL - original licence link has changed is not relivant.
35541  *
35542  * Fork - LGPL
35543  * <script type="text/javascript">
35544  */
35545  
35546
35547 /**
35548  * @class Roo.menu.BaseItem
35549  * @extends Roo.Component
35550  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
35551  * management and base configuration options shared by all menu components.
35552  * @constructor
35553  * Creates a new BaseItem
35554  * @param {Object} config Configuration options
35555  */
35556 Roo.menu.BaseItem = function(config){
35557     Roo.menu.BaseItem.superclass.constructor.call(this, config);
35558
35559     this.addEvents({
35560         /**
35561          * @event click
35562          * Fires when this item is clicked
35563          * @param {Roo.menu.BaseItem} this
35564          * @param {Roo.EventObject} e
35565          */
35566         click: true,
35567         /**
35568          * @event activate
35569          * Fires when this item is activated
35570          * @param {Roo.menu.BaseItem} this
35571          */
35572         activate : true,
35573         /**
35574          * @event deactivate
35575          * Fires when this item is deactivated
35576          * @param {Roo.menu.BaseItem} this
35577          */
35578         deactivate : true
35579     });
35580
35581     if(this.handler){
35582         this.on("click", this.handler, this.scope, true);
35583     }
35584 };
35585
35586 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
35587     /**
35588      * @cfg {Function} handler
35589      * A function that will handle the click event of this menu item (defaults to undefined)
35590      */
35591     /**
35592      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
35593      */
35594     canActivate : false,
35595     
35596      /**
35597      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
35598      */
35599     hidden: false,
35600     
35601     /**
35602      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
35603      */
35604     activeClass : "x-menu-item-active",
35605     /**
35606      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
35607      */
35608     hideOnClick : true,
35609     /**
35610      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
35611      */
35612     hideDelay : 100,
35613
35614     // private
35615     ctype: "Roo.menu.BaseItem",
35616
35617     // private
35618     actionMode : "container",
35619
35620     // private
35621     render : function(container, parentMenu){
35622         this.parentMenu = parentMenu;
35623         Roo.menu.BaseItem.superclass.render.call(this, container);
35624         this.container.menuItemId = this.id;
35625     },
35626
35627     // private
35628     onRender : function(container, position){
35629         this.el = Roo.get(this.el);
35630         container.dom.appendChild(this.el.dom);
35631     },
35632
35633     // private
35634     onClick : function(e){
35635         if(!this.disabled && this.fireEvent("click", this, e) !== false
35636                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
35637             this.handleClick(e);
35638         }else{
35639             e.stopEvent();
35640         }
35641     },
35642
35643     // private
35644     activate : function(){
35645         if(this.disabled){
35646             return false;
35647         }
35648         var li = this.container;
35649         li.addClass(this.activeClass);
35650         this.region = li.getRegion().adjust(2, 2, -2, -2);
35651         this.fireEvent("activate", this);
35652         return true;
35653     },
35654
35655     // private
35656     deactivate : function(){
35657         this.container.removeClass(this.activeClass);
35658         this.fireEvent("deactivate", this);
35659     },
35660
35661     // private
35662     shouldDeactivate : function(e){
35663         return !this.region || !this.region.contains(e.getPoint());
35664     },
35665
35666     // private
35667     handleClick : function(e){
35668         if(this.hideOnClick){
35669             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
35670         }
35671     },
35672
35673     // private
35674     expandMenu : function(autoActivate){
35675         // do nothing
35676     },
35677
35678     // private
35679     hideMenu : function(){
35680         // do nothing
35681     }
35682 });/*
35683  * Based on:
35684  * Ext JS Library 1.1.1
35685  * Copyright(c) 2006-2007, Ext JS, LLC.
35686  *
35687  * Originally Released Under LGPL - original licence link has changed is not relivant.
35688  *
35689  * Fork - LGPL
35690  * <script type="text/javascript">
35691  */
35692  
35693 /**
35694  * @class Roo.menu.Adapter
35695  * @extends Roo.menu.BaseItem
35696  * 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.
35697  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
35698  * @constructor
35699  * Creates a new Adapter
35700  * @param {Object} config Configuration options
35701  */
35702 Roo.menu.Adapter = function(component, config){
35703     Roo.menu.Adapter.superclass.constructor.call(this, config);
35704     this.component = component;
35705 };
35706 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
35707     // private
35708     canActivate : true,
35709
35710     // private
35711     onRender : function(container, position){
35712         this.component.render(container);
35713         this.el = this.component.getEl();
35714     },
35715
35716     // private
35717     activate : function(){
35718         if(this.disabled){
35719             return false;
35720         }
35721         this.component.focus();
35722         this.fireEvent("activate", this);
35723         return true;
35724     },
35725
35726     // private
35727     deactivate : function(){
35728         this.fireEvent("deactivate", this);
35729     },
35730
35731     // private
35732     disable : function(){
35733         this.component.disable();
35734         Roo.menu.Adapter.superclass.disable.call(this);
35735     },
35736
35737     // private
35738     enable : function(){
35739         this.component.enable();
35740         Roo.menu.Adapter.superclass.enable.call(this);
35741     }
35742 });/*
35743  * Based on:
35744  * Ext JS Library 1.1.1
35745  * Copyright(c) 2006-2007, Ext JS, LLC.
35746  *
35747  * Originally Released Under LGPL - original licence link has changed is not relivant.
35748  *
35749  * Fork - LGPL
35750  * <script type="text/javascript">
35751  */
35752
35753 /**
35754  * @class Roo.menu.TextItem
35755  * @extends Roo.menu.BaseItem
35756  * Adds a static text string to a menu, usually used as either a heading or group separator.
35757  * Note: old style constructor with text is still supported.
35758  * 
35759  * @constructor
35760  * Creates a new TextItem
35761  * @param {Object} cfg Configuration
35762  */
35763 Roo.menu.TextItem = function(cfg){
35764     if (typeof(cfg) == 'string') {
35765         this.text = cfg;
35766     } else {
35767         Roo.apply(this,cfg);
35768     }
35769     
35770     Roo.menu.TextItem.superclass.constructor.call(this);
35771 };
35772
35773 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
35774     /**
35775      * @cfg {Boolean} text Text to show on item.
35776      */
35777     text : '',
35778     
35779     /**
35780      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35781      */
35782     hideOnClick : false,
35783     /**
35784      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35785      */
35786     itemCls : "x-menu-text",
35787
35788     // private
35789     onRender : function(){
35790         var s = document.createElement("span");
35791         s.className = this.itemCls;
35792         s.innerHTML = this.text;
35793         this.el = s;
35794         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35795     }
35796 });/*
35797  * Based on:
35798  * Ext JS Library 1.1.1
35799  * Copyright(c) 2006-2007, Ext JS, LLC.
35800  *
35801  * Originally Released Under LGPL - original licence link has changed is not relivant.
35802  *
35803  * Fork - LGPL
35804  * <script type="text/javascript">
35805  */
35806
35807 /**
35808  * @class Roo.menu.Separator
35809  * @extends Roo.menu.BaseItem
35810  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35811  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35812  * @constructor
35813  * @param {Object} config Configuration options
35814  */
35815 Roo.menu.Separator = function(config){
35816     Roo.menu.Separator.superclass.constructor.call(this, config);
35817 };
35818
35819 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35820     /**
35821      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35822      */
35823     itemCls : "x-menu-sep",
35824     /**
35825      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35826      */
35827     hideOnClick : false,
35828
35829     // private
35830     onRender : function(li){
35831         var s = document.createElement("span");
35832         s.className = this.itemCls;
35833         s.innerHTML = "&#160;";
35834         this.el = s;
35835         li.addClass("x-menu-sep-li");
35836         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35837     }
35838 });/*
35839  * Based on:
35840  * Ext JS Library 1.1.1
35841  * Copyright(c) 2006-2007, Ext JS, LLC.
35842  *
35843  * Originally Released Under LGPL - original licence link has changed is not relivant.
35844  *
35845  * Fork - LGPL
35846  * <script type="text/javascript">
35847  */
35848 /**
35849  * @class Roo.menu.Item
35850  * @extends Roo.menu.BaseItem
35851  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35852  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35853  * activation and click handling.
35854  * @constructor
35855  * Creates a new Item
35856  * @param {Object} config Configuration options
35857  */
35858 Roo.menu.Item = function(config){
35859     Roo.menu.Item.superclass.constructor.call(this, config);
35860     if(this.menu){
35861         this.menu = Roo.menu.MenuMgr.get(this.menu);
35862     }
35863 };
35864 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35865     
35866     /**
35867      * @cfg {String} text
35868      * The text to show on the menu item.
35869      */
35870     text: '',
35871      /**
35872      * @cfg {String} HTML to render in menu
35873      * The text to show on the menu item (HTML version).
35874      */
35875     html: '',
35876     /**
35877      * @cfg {String} icon
35878      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35879      */
35880     icon: undefined,
35881     /**
35882      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35883      */
35884     itemCls : "x-menu-item",
35885     /**
35886      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35887      */
35888     canActivate : true,
35889     /**
35890      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35891      */
35892     showDelay: 200,
35893     // doc'd in BaseItem
35894     hideDelay: 200,
35895
35896     // private
35897     ctype: "Roo.menu.Item",
35898     
35899     // private
35900     onRender : function(container, position){
35901         var el = document.createElement("a");
35902         el.hideFocus = true;
35903         el.unselectable = "on";
35904         el.href = this.href || "#";
35905         if(this.hrefTarget){
35906             el.target = this.hrefTarget;
35907         }
35908         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35909         
35910         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35911         
35912         el.innerHTML = String.format(
35913                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35914                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35915         this.el = el;
35916         Roo.menu.Item.superclass.onRender.call(this, container, position);
35917     },
35918
35919     /**
35920      * Sets the text to display in this menu item
35921      * @param {String} text The text to display
35922      * @param {Boolean} isHTML true to indicate text is pure html.
35923      */
35924     setText : function(text, isHTML){
35925         if (isHTML) {
35926             this.html = text;
35927         } else {
35928             this.text = text;
35929             this.html = '';
35930         }
35931         if(this.rendered){
35932             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35933      
35934             this.el.update(String.format(
35935                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35936                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35937             this.parentMenu.autoWidth();
35938         }
35939     },
35940
35941     // private
35942     handleClick : function(e){
35943         if(!this.href){ // if no link defined, stop the event automatically
35944             e.stopEvent();
35945         }
35946         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35947     },
35948
35949     // private
35950     activate : function(autoExpand){
35951         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35952             this.focus();
35953             if(autoExpand){
35954                 this.expandMenu();
35955             }
35956         }
35957         return true;
35958     },
35959
35960     // private
35961     shouldDeactivate : function(e){
35962         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35963             if(this.menu && this.menu.isVisible()){
35964                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35965             }
35966             return true;
35967         }
35968         return false;
35969     },
35970
35971     // private
35972     deactivate : function(){
35973         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35974         this.hideMenu();
35975     },
35976
35977     // private
35978     expandMenu : function(autoActivate){
35979         if(!this.disabled && this.menu){
35980             clearTimeout(this.hideTimer);
35981             delete this.hideTimer;
35982             if(!this.menu.isVisible() && !this.showTimer){
35983                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35984             }else if (this.menu.isVisible() && autoActivate){
35985                 this.menu.tryActivate(0, 1);
35986             }
35987         }
35988     },
35989
35990     // private
35991     deferExpand : function(autoActivate){
35992         delete this.showTimer;
35993         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35994         if(autoActivate){
35995             this.menu.tryActivate(0, 1);
35996         }
35997     },
35998
35999     // private
36000     hideMenu : function(){
36001         clearTimeout(this.showTimer);
36002         delete this.showTimer;
36003         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36004             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36005         }
36006     },
36007
36008     // private
36009     deferHide : function(){
36010         delete this.hideTimer;
36011         this.menu.hide();
36012     }
36013 });/*
36014  * Based on:
36015  * Ext JS Library 1.1.1
36016  * Copyright(c) 2006-2007, Ext JS, LLC.
36017  *
36018  * Originally Released Under LGPL - original licence link has changed is not relivant.
36019  *
36020  * Fork - LGPL
36021  * <script type="text/javascript">
36022  */
36023  
36024 /**
36025  * @class Roo.menu.CheckItem
36026  * @extends Roo.menu.Item
36027  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36028  * @constructor
36029  * Creates a new CheckItem
36030  * @param {Object} config Configuration options
36031  */
36032 Roo.menu.CheckItem = function(config){
36033     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36034     this.addEvents({
36035         /**
36036          * @event beforecheckchange
36037          * Fires before the checked value is set, providing an opportunity to cancel if needed
36038          * @param {Roo.menu.CheckItem} this
36039          * @param {Boolean} checked The new checked value that will be set
36040          */
36041         "beforecheckchange" : true,
36042         /**
36043          * @event checkchange
36044          * Fires after the checked value has been set
36045          * @param {Roo.menu.CheckItem} this
36046          * @param {Boolean} checked The checked value that was set
36047          */
36048         "checkchange" : true
36049     });
36050     if(this.checkHandler){
36051         this.on('checkchange', this.checkHandler, this.scope);
36052     }
36053 };
36054 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36055     /**
36056      * @cfg {String} group
36057      * All check items with the same group name will automatically be grouped into a single-select
36058      * radio button group (defaults to '')
36059      */
36060     /**
36061      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36062      */
36063     itemCls : "x-menu-item x-menu-check-item",
36064     /**
36065      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36066      */
36067     groupClass : "x-menu-group-item",
36068
36069     /**
36070      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36071      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36072      * initialized with checked = true will be rendered as checked.
36073      */
36074     checked: false,
36075
36076     // private
36077     ctype: "Roo.menu.CheckItem",
36078
36079     // private
36080     onRender : function(c){
36081         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36082         if(this.group){
36083             this.el.addClass(this.groupClass);
36084         }
36085         Roo.menu.MenuMgr.registerCheckable(this);
36086         if(this.checked){
36087             this.checked = false;
36088             this.setChecked(true, true);
36089         }
36090     },
36091
36092     // private
36093     destroy : function(){
36094         if(this.rendered){
36095             Roo.menu.MenuMgr.unregisterCheckable(this);
36096         }
36097         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36098     },
36099
36100     /**
36101      * Set the checked state of this item
36102      * @param {Boolean} checked The new checked value
36103      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36104      */
36105     setChecked : function(state, suppressEvent){
36106         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36107             if(this.container){
36108                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36109             }
36110             this.checked = state;
36111             if(suppressEvent !== true){
36112                 this.fireEvent("checkchange", this, state);
36113             }
36114         }
36115     },
36116
36117     // private
36118     handleClick : function(e){
36119        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36120            this.setChecked(!this.checked);
36121        }
36122        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36123     }
36124 });/*
36125  * Based on:
36126  * Ext JS Library 1.1.1
36127  * Copyright(c) 2006-2007, Ext JS, LLC.
36128  *
36129  * Originally Released Under LGPL - original licence link has changed is not relivant.
36130  *
36131  * Fork - LGPL
36132  * <script type="text/javascript">
36133  */
36134  
36135 /**
36136  * @class Roo.menu.DateItem
36137  * @extends Roo.menu.Adapter
36138  * A menu item that wraps the {@link Roo.DatPicker} component.
36139  * @constructor
36140  * Creates a new DateItem
36141  * @param {Object} config Configuration options
36142  */
36143 Roo.menu.DateItem = function(config){
36144     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36145     /** The Roo.DatePicker object @type Roo.DatePicker */
36146     this.picker = this.component;
36147     this.addEvents({select: true});
36148     
36149     this.picker.on("render", function(picker){
36150         picker.getEl().swallowEvent("click");
36151         picker.container.addClass("x-menu-date-item");
36152     });
36153
36154     this.picker.on("select", this.onSelect, this);
36155 };
36156
36157 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36158     // private
36159     onSelect : function(picker, date){
36160         this.fireEvent("select", this, date, picker);
36161         Roo.menu.DateItem.superclass.handleClick.call(this);
36162     }
36163 });/*
36164  * Based on:
36165  * Ext JS Library 1.1.1
36166  * Copyright(c) 2006-2007, Ext JS, LLC.
36167  *
36168  * Originally Released Under LGPL - original licence link has changed is not relivant.
36169  *
36170  * Fork - LGPL
36171  * <script type="text/javascript">
36172  */
36173  
36174 /**
36175  * @class Roo.menu.ColorItem
36176  * @extends Roo.menu.Adapter
36177  * A menu item that wraps the {@link Roo.ColorPalette} component.
36178  * @constructor
36179  * Creates a new ColorItem
36180  * @param {Object} config Configuration options
36181  */
36182 Roo.menu.ColorItem = function(config){
36183     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36184     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36185     this.palette = this.component;
36186     this.relayEvents(this.palette, ["select"]);
36187     if(this.selectHandler){
36188         this.on('select', this.selectHandler, this.scope);
36189     }
36190 };
36191 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36192  * Based on:
36193  * Ext JS Library 1.1.1
36194  * Copyright(c) 2006-2007, Ext JS, LLC.
36195  *
36196  * Originally Released Under LGPL - original licence link has changed is not relivant.
36197  *
36198  * Fork - LGPL
36199  * <script type="text/javascript">
36200  */
36201  
36202
36203 /**
36204  * @class Roo.menu.DateMenu
36205  * @extends Roo.menu.Menu
36206  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36207  * @constructor
36208  * Creates a new DateMenu
36209  * @param {Object} config Configuration options
36210  */
36211 Roo.menu.DateMenu = function(config){
36212     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36213     this.plain = true;
36214     var di = new Roo.menu.DateItem(config);
36215     this.add(di);
36216     /**
36217      * The {@link Roo.DatePicker} instance for this DateMenu
36218      * @type DatePicker
36219      */
36220     this.picker = di.picker;
36221     /**
36222      * @event select
36223      * @param {DatePicker} picker
36224      * @param {Date} date
36225      */
36226     this.relayEvents(di, ["select"]);
36227     this.on('beforeshow', function(){
36228         if(this.picker){
36229             this.picker.hideMonthPicker(false);
36230         }
36231     }, this);
36232 };
36233 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36234     cls:'x-date-menu'
36235 });/*
36236  * Based on:
36237  * Ext JS Library 1.1.1
36238  * Copyright(c) 2006-2007, Ext JS, LLC.
36239  *
36240  * Originally Released Under LGPL - original licence link has changed is not relivant.
36241  *
36242  * Fork - LGPL
36243  * <script type="text/javascript">
36244  */
36245  
36246
36247 /**
36248  * @class Roo.menu.ColorMenu
36249  * @extends Roo.menu.Menu
36250  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36251  * @constructor
36252  * Creates a new ColorMenu
36253  * @param {Object} config Configuration options
36254  */
36255 Roo.menu.ColorMenu = function(config){
36256     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36257     this.plain = true;
36258     var ci = new Roo.menu.ColorItem(config);
36259     this.add(ci);
36260     /**
36261      * The {@link Roo.ColorPalette} instance for this ColorMenu
36262      * @type ColorPalette
36263      */
36264     this.palette = ci.palette;
36265     /**
36266      * @event select
36267      * @param {ColorPalette} palette
36268      * @param {String} color
36269      */
36270     this.relayEvents(ci, ["select"]);
36271 };
36272 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36273  * Based on:
36274  * Ext JS Library 1.1.1
36275  * Copyright(c) 2006-2007, Ext JS, LLC.
36276  *
36277  * Originally Released Under LGPL - original licence link has changed is not relivant.
36278  *
36279  * Fork - LGPL
36280  * <script type="text/javascript">
36281  */
36282  
36283 /**
36284  * @class Roo.form.Field
36285  * @extends Roo.BoxComponent
36286  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36287  * @constructor
36288  * Creates a new Field
36289  * @param {Object} config Configuration options
36290  */
36291 Roo.form.Field = function(config){
36292     Roo.form.Field.superclass.constructor.call(this, config);
36293 };
36294
36295 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36296     /**
36297      * @cfg {String} fieldLabel Label to use when rendering a form.
36298      */
36299        /**
36300      * @cfg {String} qtip Mouse over tip
36301      */
36302      
36303     /**
36304      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36305      */
36306     invalidClass : "x-form-invalid",
36307     /**
36308      * @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")
36309      */
36310     invalidText : "The value in this field is invalid",
36311     /**
36312      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36313      */
36314     focusClass : "x-form-focus",
36315     /**
36316      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36317       automatic validation (defaults to "keyup").
36318      */
36319     validationEvent : "keyup",
36320     /**
36321      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36322      */
36323     validateOnBlur : true,
36324     /**
36325      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36326      */
36327     validationDelay : 250,
36328     /**
36329      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36330      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36331      */
36332     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36333     /**
36334      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36335      */
36336     fieldClass : "x-form-field",
36337     /**
36338      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36339      *<pre>
36340 Value         Description
36341 -----------   ----------------------------------------------------------------------
36342 qtip          Display a quick tip when the user hovers over the field
36343 title         Display a default browser title attribute popup
36344 under         Add a block div beneath the field containing the error text
36345 side          Add an error icon to the right of the field with a popup on hover
36346 [element id]  Add the error text directly to the innerHTML of the specified element
36347 </pre>
36348      */
36349     msgTarget : 'qtip',
36350     /**
36351      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36352      */
36353     msgFx : 'normal',
36354
36355     /**
36356      * @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.
36357      */
36358     readOnly : false,
36359
36360     /**
36361      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36362      */
36363     disabled : false,
36364
36365     /**
36366      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36367      */
36368     inputType : undefined,
36369     
36370     /**
36371      * @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).
36372          */
36373         tabIndex : undefined,
36374         
36375     // private
36376     isFormField : true,
36377
36378     // private
36379     hasFocus : false,
36380     /**
36381      * @property {Roo.Element} fieldEl
36382      * Element Containing the rendered Field (with label etc.)
36383      */
36384     /**
36385      * @cfg {Mixed} value A value to initialize this field with.
36386      */
36387     value : undefined,
36388
36389     /**
36390      * @cfg {String} name The field's HTML name attribute.
36391      */
36392     /**
36393      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36394      */
36395
36396         // private ??
36397         initComponent : function(){
36398         Roo.form.Field.superclass.initComponent.call(this);
36399         this.addEvents({
36400             /**
36401              * @event focus
36402              * Fires when this field receives input focus.
36403              * @param {Roo.form.Field} this
36404              */
36405             focus : true,
36406             /**
36407              * @event blur
36408              * Fires when this field loses input focus.
36409              * @param {Roo.form.Field} this
36410              */
36411             blur : true,
36412             /**
36413              * @event specialkey
36414              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36415              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36416              * @param {Roo.form.Field} this
36417              * @param {Roo.EventObject} e The event object
36418              */
36419             specialkey : true,
36420             /**
36421              * @event change
36422              * Fires just before the field blurs if the field value has changed.
36423              * @param {Roo.form.Field} this
36424              * @param {Mixed} newValue The new value
36425              * @param {Mixed} oldValue The original value
36426              */
36427             change : true,
36428             /**
36429              * @event invalid
36430              * Fires after the field has been marked as invalid.
36431              * @param {Roo.form.Field} this
36432              * @param {String} msg The validation message
36433              */
36434             invalid : true,
36435             /**
36436              * @event valid
36437              * Fires after the field has been validated with no errors.
36438              * @param {Roo.form.Field} this
36439              */
36440             valid : true,
36441              /**
36442              * @event keyup
36443              * Fires after the key up
36444              * @param {Roo.form.Field} this
36445              * @param {Roo.EventObject}  e The event Object
36446              */
36447             keyup : true
36448         });
36449     },
36450
36451     /**
36452      * Returns the name attribute of the field if available
36453      * @return {String} name The field name
36454      */
36455     getName: function(){
36456          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36457     },
36458
36459     // private
36460     onRender : function(ct, position){
36461         Roo.form.Field.superclass.onRender.call(this, ct, position);
36462         if(!this.el){
36463             var cfg = this.getAutoCreate();
36464             if(!cfg.name){
36465                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
36466             }
36467             if (!cfg.name.length) {
36468                 delete cfg.name;
36469             }
36470             if(this.inputType){
36471                 cfg.type = this.inputType;
36472             }
36473             this.el = ct.createChild(cfg, position);
36474         }
36475         var type = this.el.dom.type;
36476         if(type){
36477             if(type == 'password'){
36478                 type = 'text';
36479             }
36480             this.el.addClass('x-form-'+type);
36481         }
36482         if(this.readOnly){
36483             this.el.dom.readOnly = true;
36484         }
36485         if(this.tabIndex !== undefined){
36486             this.el.dom.setAttribute('tabIndex', this.tabIndex);
36487         }
36488
36489         this.el.addClass([this.fieldClass, this.cls]);
36490         this.initValue();
36491     },
36492
36493     /**
36494      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
36495      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
36496      * @return {Roo.form.Field} this
36497      */
36498     applyTo : function(target){
36499         this.allowDomMove = false;
36500         this.el = Roo.get(target);
36501         this.render(this.el.dom.parentNode);
36502         return this;
36503     },
36504
36505     // private
36506     initValue : function(){
36507         if(this.value !== undefined){
36508             this.setValue(this.value);
36509         }else if(this.el.dom.value.length > 0){
36510             this.setValue(this.el.dom.value);
36511         }
36512     },
36513
36514     /**
36515      * Returns true if this field has been changed since it was originally loaded and is not disabled.
36516      */
36517     isDirty : function() {
36518         if(this.disabled) {
36519             return false;
36520         }
36521         return String(this.getValue()) !== String(this.originalValue);
36522     },
36523
36524     // private
36525     afterRender : function(){
36526         Roo.form.Field.superclass.afterRender.call(this);
36527         this.initEvents();
36528     },
36529
36530     // private
36531     fireKey : function(e){
36532         //Roo.log('field ' + e.getKey());
36533         if(e.isNavKeyPress()){
36534             this.fireEvent("specialkey", this, e);
36535         }
36536     },
36537
36538     /**
36539      * Resets the current field value to the originally loaded value and clears any validation messages
36540      */
36541     reset : function(){
36542         this.setValue(this.originalValue);
36543         this.clearInvalid();
36544     },
36545
36546     // private
36547     initEvents : function(){
36548         // safari killled keypress - so keydown is now used..
36549         this.el.on("keydown" , this.fireKey,  this);
36550         this.el.on("focus", this.onFocus,  this);
36551         this.el.on("blur", this.onBlur,  this);
36552         this.el.relayEvent('keyup', this);
36553
36554         // reference to original value for reset
36555         this.originalValue = this.getValue();
36556     },
36557
36558     // private
36559     onFocus : function(){
36560         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36561             this.el.addClass(this.focusClass);
36562         }
36563         if(!this.hasFocus){
36564             this.hasFocus = true;
36565             this.startValue = this.getValue();
36566             this.fireEvent("focus", this);
36567         }
36568     },
36569
36570     beforeBlur : Roo.emptyFn,
36571
36572     // private
36573     onBlur : function(){
36574         this.beforeBlur();
36575         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36576             this.el.removeClass(this.focusClass);
36577         }
36578         this.hasFocus = false;
36579         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
36580             this.validate();
36581         }
36582         var v = this.getValue();
36583         if(String(v) !== String(this.startValue)){
36584             this.fireEvent('change', this, v, this.startValue);
36585         }
36586         this.fireEvent("blur", this);
36587     },
36588
36589     /**
36590      * Returns whether or not the field value is currently valid
36591      * @param {Boolean} preventMark True to disable marking the field invalid
36592      * @return {Boolean} True if the value is valid, else false
36593      */
36594     isValid : function(preventMark){
36595         if(this.disabled){
36596             return true;
36597         }
36598         var restore = this.preventMark;
36599         this.preventMark = preventMark === true;
36600         var v = this.validateValue(this.processValue(this.getRawValue()));
36601         this.preventMark = restore;
36602         return v;
36603     },
36604
36605     /**
36606      * Validates the field value
36607      * @return {Boolean} True if the value is valid, else false
36608      */
36609     validate : function(){
36610         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
36611             this.clearInvalid();
36612             return true;
36613         }
36614         return false;
36615     },
36616
36617     processValue : function(value){
36618         return value;
36619     },
36620
36621     // private
36622     // Subclasses should provide the validation implementation by overriding this
36623     validateValue : function(value){
36624         return true;
36625     },
36626
36627     /**
36628      * Mark this field as invalid
36629      * @param {String} msg The validation message
36630      */
36631     markInvalid : function(msg){
36632         if(!this.rendered || this.preventMark){ // not rendered
36633             return;
36634         }
36635         this.el.addClass(this.invalidClass);
36636         msg = msg || this.invalidText;
36637         switch(this.msgTarget){
36638             case 'qtip':
36639                 this.el.dom.qtip = msg;
36640                 this.el.dom.qclass = 'x-form-invalid-tip';
36641                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
36642                     Roo.QuickTips.enable();
36643                 }
36644                 break;
36645             case 'title':
36646                 this.el.dom.title = msg;
36647                 break;
36648             case 'under':
36649                 if(!this.errorEl){
36650                     var elp = this.el.findParent('.x-form-element', 5, true);
36651                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
36652                     this.errorEl.setWidth(elp.getWidth(true)-20);
36653                 }
36654                 this.errorEl.update(msg);
36655                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
36656                 break;
36657             case 'side':
36658                 if(!this.errorIcon){
36659                     var elp = this.el.findParent('.x-form-element', 5, true);
36660                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
36661                 }
36662                 this.alignErrorIcon();
36663                 this.errorIcon.dom.qtip = msg;
36664                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
36665                 this.errorIcon.show();
36666                 this.on('resize', this.alignErrorIcon, this);
36667                 break;
36668             default:
36669                 var t = Roo.getDom(this.msgTarget);
36670                 t.innerHTML = msg;
36671                 t.style.display = this.msgDisplay;
36672                 break;
36673         }
36674         this.fireEvent('invalid', this, msg);
36675     },
36676
36677     // private
36678     alignErrorIcon : function(){
36679         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
36680     },
36681
36682     /**
36683      * Clear any invalid styles/messages for this field
36684      */
36685     clearInvalid : function(){
36686         if(!this.rendered || this.preventMark){ // not rendered
36687             return;
36688         }
36689         this.el.removeClass(this.invalidClass);
36690         switch(this.msgTarget){
36691             case 'qtip':
36692                 this.el.dom.qtip = '';
36693                 break;
36694             case 'title':
36695                 this.el.dom.title = '';
36696                 break;
36697             case 'under':
36698                 if(this.errorEl){
36699                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
36700                 }
36701                 break;
36702             case 'side':
36703                 if(this.errorIcon){
36704                     this.errorIcon.dom.qtip = '';
36705                     this.errorIcon.hide();
36706                     this.un('resize', this.alignErrorIcon, this);
36707                 }
36708                 break;
36709             default:
36710                 var t = Roo.getDom(this.msgTarget);
36711                 t.innerHTML = '';
36712                 t.style.display = 'none';
36713                 break;
36714         }
36715         this.fireEvent('valid', this);
36716     },
36717
36718     /**
36719      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
36720      * @return {Mixed} value The field value
36721      */
36722     getRawValue : function(){
36723         var v = this.el.getValue();
36724         
36725         return v;
36726     },
36727
36728     /**
36729      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
36730      * @return {Mixed} value The field value
36731      */
36732     getValue : function(){
36733         var v = this.el.getValue();
36734          
36735         return v;
36736     },
36737
36738     /**
36739      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
36740      * @param {Mixed} value The value to set
36741      */
36742     setRawValue : function(v){
36743         return this.el.dom.value = (v === null || v === undefined ? '' : v);
36744     },
36745
36746     /**
36747      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
36748      * @param {Mixed} value The value to set
36749      */
36750     setValue : function(v){
36751         this.value = v;
36752         if(this.rendered){
36753             this.el.dom.value = (v === null || v === undefined ? '' : v);
36754              this.validate();
36755         }
36756     },
36757
36758     adjustSize : function(w, h){
36759         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
36760         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
36761         return s;
36762     },
36763
36764     adjustWidth : function(tag, w){
36765         tag = tag.toLowerCase();
36766         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
36767             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
36768                 if(tag == 'input'){
36769                     return w + 2;
36770                 }
36771                 if(tag == 'textarea'){
36772                     return w-2;
36773                 }
36774             }else if(Roo.isOpera){
36775                 if(tag == 'input'){
36776                     return w + 2;
36777                 }
36778                 if(tag == 'textarea'){
36779                     return w-2;
36780                 }
36781             }
36782         }
36783         return w;
36784     }
36785 });
36786
36787
36788 // anything other than normal should be considered experimental
36789 Roo.form.Field.msgFx = {
36790     normal : {
36791         show: function(msgEl, f){
36792             msgEl.setDisplayed('block');
36793         },
36794
36795         hide : function(msgEl, f){
36796             msgEl.setDisplayed(false).update('');
36797         }
36798     },
36799
36800     slide : {
36801         show: function(msgEl, f){
36802             msgEl.slideIn('t', {stopFx:true});
36803         },
36804
36805         hide : function(msgEl, f){
36806             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36807         }
36808     },
36809
36810     slideRight : {
36811         show: function(msgEl, f){
36812             msgEl.fixDisplay();
36813             msgEl.alignTo(f.el, 'tl-tr');
36814             msgEl.slideIn('l', {stopFx:true});
36815         },
36816
36817         hide : function(msgEl, f){
36818             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36819         }
36820     }
36821 };/*
36822  * Based on:
36823  * Ext JS Library 1.1.1
36824  * Copyright(c) 2006-2007, Ext JS, LLC.
36825  *
36826  * Originally Released Under LGPL - original licence link has changed is not relivant.
36827  *
36828  * Fork - LGPL
36829  * <script type="text/javascript">
36830  */
36831  
36832
36833 /**
36834  * @class Roo.form.TextField
36835  * @extends Roo.form.Field
36836  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36837  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36838  * @constructor
36839  * Creates a new TextField
36840  * @param {Object} config Configuration options
36841  */
36842 Roo.form.TextField = function(config){
36843     Roo.form.TextField.superclass.constructor.call(this, config);
36844     this.addEvents({
36845         /**
36846          * @event autosize
36847          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36848          * according to the default logic, but this event provides a hook for the developer to apply additional
36849          * logic at runtime to resize the field if needed.
36850              * @param {Roo.form.Field} this This text field
36851              * @param {Number} width The new field width
36852              */
36853         autosize : true
36854     });
36855 };
36856
36857 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36858     /**
36859      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36860      */
36861     grow : false,
36862     /**
36863      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36864      */
36865     growMin : 30,
36866     /**
36867      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36868      */
36869     growMax : 800,
36870     /**
36871      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36872      */
36873     vtype : null,
36874     /**
36875      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36876      */
36877     maskRe : null,
36878     /**
36879      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36880      */
36881     disableKeyFilter : false,
36882     /**
36883      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36884      */
36885     allowBlank : true,
36886     /**
36887      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36888      */
36889     minLength : 0,
36890     /**
36891      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36892      */
36893     maxLength : Number.MAX_VALUE,
36894     /**
36895      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36896      */
36897     minLengthText : "The minimum length for this field is {0}",
36898     /**
36899      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36900      */
36901     maxLengthText : "The maximum length for this field is {0}",
36902     /**
36903      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36904      */
36905     selectOnFocus : false,
36906     /**
36907      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36908      */
36909     blankText : "This field is required",
36910     /**
36911      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36912      * If available, this function will be called only after the basic validators all return true, and will be passed the
36913      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36914      */
36915     validator : null,
36916     /**
36917      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36918      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36919      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36920      */
36921     regex : null,
36922     /**
36923      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36924      */
36925     regexText : "",
36926     /**
36927      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
36928      */
36929     emptyText : null,
36930    
36931
36932     // private
36933     initEvents : function()
36934     {
36935         if (this.emptyText) {
36936             this.el.attr('placeholder', this.emptyText);
36937         }
36938         
36939         Roo.form.TextField.superclass.initEvents.call(this);
36940         if(this.validationEvent == 'keyup'){
36941             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36942             this.el.on('keyup', this.filterValidation, this);
36943         }
36944         else if(this.validationEvent !== false){
36945             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36946         }
36947         
36948         if(this.selectOnFocus){
36949             this.on("focus", this.preFocus, this);
36950             
36951         }
36952         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36953             this.el.on("keypress", this.filterKeys, this);
36954         }
36955         if(this.grow){
36956             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36957             this.el.on("click", this.autoSize,  this);
36958         }
36959         if(this.el.is('input[type=password]') && Roo.isSafari){
36960             this.el.on('keydown', this.SafariOnKeyDown, this);
36961         }
36962     },
36963
36964     processValue : function(value){
36965         if(this.stripCharsRe){
36966             var newValue = value.replace(this.stripCharsRe, '');
36967             if(newValue !== value){
36968                 this.setRawValue(newValue);
36969                 return newValue;
36970             }
36971         }
36972         return value;
36973     },
36974
36975     filterValidation : function(e){
36976         if(!e.isNavKeyPress()){
36977             this.validationTask.delay(this.validationDelay);
36978         }
36979     },
36980
36981     // private
36982     onKeyUp : function(e){
36983         if(!e.isNavKeyPress()){
36984             this.autoSize();
36985         }
36986     },
36987
36988     /**
36989      * Resets the current field value to the originally-loaded value and clears any validation messages.
36990      *  
36991      */
36992     reset : function(){
36993         Roo.form.TextField.superclass.reset.call(this);
36994        
36995     },
36996
36997     
36998     // private
36999     preFocus : function(){
37000         
37001         if(this.selectOnFocus){
37002             this.el.dom.select();
37003         }
37004     },
37005
37006     
37007     // private
37008     filterKeys : function(e){
37009         var k = e.getKey();
37010         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37011             return;
37012         }
37013         var c = e.getCharCode(), cc = String.fromCharCode(c);
37014         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37015             return;
37016         }
37017         if(!this.maskRe.test(cc)){
37018             e.stopEvent();
37019         }
37020     },
37021
37022     setValue : function(v){
37023         
37024         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37025         
37026         this.autoSize();
37027     },
37028
37029     /**
37030      * Validates a value according to the field's validation rules and marks the field as invalid
37031      * if the validation fails
37032      * @param {Mixed} value The value to validate
37033      * @return {Boolean} True if the value is valid, else false
37034      */
37035     validateValue : function(value){
37036         if(value.length < 1)  { // if it's blank
37037              if(this.allowBlank){
37038                 this.clearInvalid();
37039                 return true;
37040              }else{
37041                 this.markInvalid(this.blankText);
37042                 return false;
37043              }
37044         }
37045         if(value.length < this.minLength){
37046             this.markInvalid(String.format(this.minLengthText, this.minLength));
37047             return false;
37048         }
37049         if(value.length > this.maxLength){
37050             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37051             return false;
37052         }
37053         if(this.vtype){
37054             var vt = Roo.form.VTypes;
37055             if(!vt[this.vtype](value, this)){
37056                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37057                 return false;
37058             }
37059         }
37060         if(typeof this.validator == "function"){
37061             var msg = this.validator(value);
37062             if(msg !== true){
37063                 this.markInvalid(msg);
37064                 return false;
37065             }
37066         }
37067         if(this.regex && !this.regex.test(value)){
37068             this.markInvalid(this.regexText);
37069             return false;
37070         }
37071         return true;
37072     },
37073
37074     /**
37075      * Selects text in this field
37076      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37077      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37078      */
37079     selectText : function(start, end){
37080         var v = this.getRawValue();
37081         if(v.length > 0){
37082             start = start === undefined ? 0 : start;
37083             end = end === undefined ? v.length : end;
37084             var d = this.el.dom;
37085             if(d.setSelectionRange){
37086                 d.setSelectionRange(start, end);
37087             }else if(d.createTextRange){
37088                 var range = d.createTextRange();
37089                 range.moveStart("character", start);
37090                 range.moveEnd("character", v.length-end);
37091                 range.select();
37092             }
37093         }
37094     },
37095
37096     /**
37097      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37098      * This only takes effect if grow = true, and fires the autosize event.
37099      */
37100     autoSize : function(){
37101         if(!this.grow || !this.rendered){
37102             return;
37103         }
37104         if(!this.metrics){
37105             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37106         }
37107         var el = this.el;
37108         var v = el.dom.value;
37109         var d = document.createElement('div');
37110         d.appendChild(document.createTextNode(v));
37111         v = d.innerHTML;
37112         d = null;
37113         v += "&#160;";
37114         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37115         this.el.setWidth(w);
37116         this.fireEvent("autosize", this, w);
37117     },
37118     
37119     // private
37120     SafariOnKeyDown : function(event)
37121     {
37122         // this is a workaround for a password hang bug on chrome/ webkit.
37123         
37124         var isSelectAll = false;
37125         
37126         if(this.el.dom.selectionEnd > 0){
37127             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37128         }
37129         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37130             event.preventDefault();
37131             this.setValue('');
37132             return;
37133         }
37134         
37135         if(isSelectAll){ // backspace and delete key
37136             
37137             event.preventDefault();
37138             // this is very hacky as keydown always get's upper case.
37139             //
37140             var cc = String.fromCharCode(event.getCharCode());
37141             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37142             
37143         }
37144         
37145         
37146     }
37147 });/*
37148  * Based on:
37149  * Ext JS Library 1.1.1
37150  * Copyright(c) 2006-2007, Ext JS, LLC.
37151  *
37152  * Originally Released Under LGPL - original licence link has changed is not relivant.
37153  *
37154  * Fork - LGPL
37155  * <script type="text/javascript">
37156  */
37157  
37158 /**
37159  * @class Roo.form.Hidden
37160  * @extends Roo.form.TextField
37161  * Simple Hidden element used on forms 
37162  * 
37163  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37164  * 
37165  * @constructor
37166  * Creates a new Hidden form element.
37167  * @param {Object} config Configuration options
37168  */
37169
37170
37171
37172 // easy hidden field...
37173 Roo.form.Hidden = function(config){
37174     Roo.form.Hidden.superclass.constructor.call(this, config);
37175 };
37176   
37177 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37178     fieldLabel:      '',
37179     inputType:      'hidden',
37180     width:          50,
37181     allowBlank:     true,
37182     labelSeparator: '',
37183     hidden:         true,
37184     itemCls :       'x-form-item-display-none'
37185
37186
37187 });
37188
37189
37190 /*
37191  * Based on:
37192  * Ext JS Library 1.1.1
37193  * Copyright(c) 2006-2007, Ext JS, LLC.
37194  *
37195  * Originally Released Under LGPL - original licence link has changed is not relivant.
37196  *
37197  * Fork - LGPL
37198  * <script type="text/javascript">
37199  */
37200  
37201 /**
37202  * @class Roo.form.TriggerField
37203  * @extends Roo.form.TextField
37204  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37205  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37206  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37207  * for which you can provide a custom implementation.  For example:
37208  * <pre><code>
37209 var trigger = new Roo.form.TriggerField();
37210 trigger.onTriggerClick = myTriggerFn;
37211 trigger.applyTo('my-field');
37212 </code></pre>
37213  *
37214  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37215  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37216  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37217  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37218  * @constructor
37219  * Create a new TriggerField.
37220  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37221  * to the base TextField)
37222  */
37223 Roo.form.TriggerField = function(config){
37224     this.mimicing = false;
37225     Roo.form.TriggerField.superclass.constructor.call(this, config);
37226 };
37227
37228 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37229     /**
37230      * @cfg {String} triggerClass A CSS class to apply to the trigger
37231      */
37232     /**
37233      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37234      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37235      */
37236     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37237     /**
37238      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37239      */
37240     hideTrigger:false,
37241
37242     /** @cfg {Boolean} grow @hide */
37243     /** @cfg {Number} growMin @hide */
37244     /** @cfg {Number} growMax @hide */
37245
37246     /**
37247      * @hide 
37248      * @method
37249      */
37250     autoSize: Roo.emptyFn,
37251     // private
37252     monitorTab : true,
37253     // private
37254     deferHeight : true,
37255
37256     
37257     actionMode : 'wrap',
37258     // private
37259     onResize : function(w, h){
37260         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37261         if(typeof w == 'number'){
37262             var x = w - this.trigger.getWidth();
37263             this.el.setWidth(this.adjustWidth('input', x));
37264             this.trigger.setStyle('left', x+'px');
37265         }
37266     },
37267
37268     // private
37269     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37270
37271     // private
37272     getResizeEl : function(){
37273         return this.wrap;
37274     },
37275
37276     // private
37277     getPositionEl : function(){
37278         return this.wrap;
37279     },
37280
37281     // private
37282     alignErrorIcon : function(){
37283         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37284     },
37285
37286     // private
37287     onRender : function(ct, position){
37288         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37289         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37290         this.trigger = this.wrap.createChild(this.triggerConfig ||
37291                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37292         if(this.hideTrigger){
37293             this.trigger.setDisplayed(false);
37294         }
37295         this.initTrigger();
37296         if(!this.width){
37297             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37298         }
37299     },
37300
37301     // private
37302     initTrigger : function(){
37303         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37304         this.trigger.addClassOnOver('x-form-trigger-over');
37305         this.trigger.addClassOnClick('x-form-trigger-click');
37306     },
37307
37308     // private
37309     onDestroy : function(){
37310         if(this.trigger){
37311             this.trigger.removeAllListeners();
37312             this.trigger.remove();
37313         }
37314         if(this.wrap){
37315             this.wrap.remove();
37316         }
37317         Roo.form.TriggerField.superclass.onDestroy.call(this);
37318     },
37319
37320     // private
37321     onFocus : function(){
37322         Roo.form.TriggerField.superclass.onFocus.call(this);
37323         if(!this.mimicing){
37324             this.wrap.addClass('x-trigger-wrap-focus');
37325             this.mimicing = true;
37326             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37327             if(this.monitorTab){
37328                 this.el.on("keydown", this.checkTab, this);
37329             }
37330         }
37331     },
37332
37333     // private
37334     checkTab : function(e){
37335         if(e.getKey() == e.TAB){
37336             this.triggerBlur();
37337         }
37338     },
37339
37340     // private
37341     onBlur : function(){
37342         // do nothing
37343     },
37344
37345     // private
37346     mimicBlur : function(e, t){
37347         if(!this.wrap.contains(t) && this.validateBlur()){
37348             this.triggerBlur();
37349         }
37350     },
37351
37352     // private
37353     triggerBlur : function(){
37354         this.mimicing = false;
37355         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37356         if(this.monitorTab){
37357             this.el.un("keydown", this.checkTab, this);
37358         }
37359         this.wrap.removeClass('x-trigger-wrap-focus');
37360         Roo.form.TriggerField.superclass.onBlur.call(this);
37361     },
37362
37363     // private
37364     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37365     validateBlur : function(e, t){
37366         return true;
37367     },
37368
37369     // private
37370     onDisable : function(){
37371         Roo.form.TriggerField.superclass.onDisable.call(this);
37372         if(this.wrap){
37373             this.wrap.addClass('x-item-disabled');
37374         }
37375     },
37376
37377     // private
37378     onEnable : function(){
37379         Roo.form.TriggerField.superclass.onEnable.call(this);
37380         if(this.wrap){
37381             this.wrap.removeClass('x-item-disabled');
37382         }
37383     },
37384
37385     // private
37386     onShow : function(){
37387         var ae = this.getActionEl();
37388         
37389         if(ae){
37390             ae.dom.style.display = '';
37391             ae.dom.style.visibility = 'visible';
37392         }
37393     },
37394
37395     // private
37396     
37397     onHide : function(){
37398         var ae = this.getActionEl();
37399         ae.dom.style.display = 'none';
37400     },
37401
37402     /**
37403      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37404      * by an implementing function.
37405      * @method
37406      * @param {EventObject} e
37407      */
37408     onTriggerClick : Roo.emptyFn
37409 });
37410
37411 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37412 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37413 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37414 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37415     initComponent : function(){
37416         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37417
37418         this.triggerConfig = {
37419             tag:'span', cls:'x-form-twin-triggers', cn:[
37420             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37421             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37422         ]};
37423     },
37424
37425     getTrigger : function(index){
37426         return this.triggers[index];
37427     },
37428
37429     initTrigger : function(){
37430         var ts = this.trigger.select('.x-form-trigger', true);
37431         this.wrap.setStyle('overflow', 'hidden');
37432         var triggerField = this;
37433         ts.each(function(t, all, index){
37434             t.hide = function(){
37435                 var w = triggerField.wrap.getWidth();
37436                 this.dom.style.display = 'none';
37437                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37438             };
37439             t.show = function(){
37440                 var w = triggerField.wrap.getWidth();
37441                 this.dom.style.display = '';
37442                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37443             };
37444             var triggerIndex = 'Trigger'+(index+1);
37445
37446             if(this['hide'+triggerIndex]){
37447                 t.dom.style.display = 'none';
37448             }
37449             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37450             t.addClassOnOver('x-form-trigger-over');
37451             t.addClassOnClick('x-form-trigger-click');
37452         }, this);
37453         this.triggers = ts.elements;
37454     },
37455
37456     onTrigger1Click : Roo.emptyFn,
37457     onTrigger2Click : Roo.emptyFn
37458 });/*
37459  * Based on:
37460  * Ext JS Library 1.1.1
37461  * Copyright(c) 2006-2007, Ext JS, LLC.
37462  *
37463  * Originally Released Under LGPL - original licence link has changed is not relivant.
37464  *
37465  * Fork - LGPL
37466  * <script type="text/javascript">
37467  */
37468  
37469 /**
37470  * @class Roo.form.TextArea
37471  * @extends Roo.form.TextField
37472  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
37473  * support for auto-sizing.
37474  * @constructor
37475  * Creates a new TextArea
37476  * @param {Object} config Configuration options
37477  */
37478 Roo.form.TextArea = function(config){
37479     Roo.form.TextArea.superclass.constructor.call(this, config);
37480     // these are provided exchanges for backwards compat
37481     // minHeight/maxHeight were replaced by growMin/growMax to be
37482     // compatible with TextField growing config values
37483     if(this.minHeight !== undefined){
37484         this.growMin = this.minHeight;
37485     }
37486     if(this.maxHeight !== undefined){
37487         this.growMax = this.maxHeight;
37488     }
37489 };
37490
37491 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
37492     /**
37493      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
37494      */
37495     growMin : 60,
37496     /**
37497      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
37498      */
37499     growMax: 1000,
37500     /**
37501      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
37502      * in the field (equivalent to setting overflow: hidden, defaults to false)
37503      */
37504     preventScrollbars: false,
37505     /**
37506      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37507      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
37508      */
37509
37510     // private
37511     onRender : function(ct, position){
37512         if(!this.el){
37513             this.defaultAutoCreate = {
37514                 tag: "textarea",
37515                 style:"width:300px;height:60px;",
37516                 autocomplete: "off"
37517             };
37518         }
37519         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
37520         if(this.grow){
37521             this.textSizeEl = Roo.DomHelper.append(document.body, {
37522                 tag: "pre", cls: "x-form-grow-sizer"
37523             });
37524             if(this.preventScrollbars){
37525                 this.el.setStyle("overflow", "hidden");
37526             }
37527             this.el.setHeight(this.growMin);
37528         }
37529     },
37530
37531     onDestroy : function(){
37532         if(this.textSizeEl){
37533             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
37534         }
37535         Roo.form.TextArea.superclass.onDestroy.call(this);
37536     },
37537
37538     // private
37539     onKeyUp : function(e){
37540         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
37541             this.autoSize();
37542         }
37543     },
37544
37545     /**
37546      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
37547      * This only takes effect if grow = true, and fires the autosize event if the height changes.
37548      */
37549     autoSize : function(){
37550         if(!this.grow || !this.textSizeEl){
37551             return;
37552         }
37553         var el = this.el;
37554         var v = el.dom.value;
37555         var ts = this.textSizeEl;
37556
37557         ts.innerHTML = '';
37558         ts.appendChild(document.createTextNode(v));
37559         v = ts.innerHTML;
37560
37561         Roo.fly(ts).setWidth(this.el.getWidth());
37562         if(v.length < 1){
37563             v = "&#160;&#160;";
37564         }else{
37565             if(Roo.isIE){
37566                 v = v.replace(/\n/g, '<p>&#160;</p>');
37567             }
37568             v += "&#160;\n&#160;";
37569         }
37570         ts.innerHTML = v;
37571         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
37572         if(h != this.lastHeight){
37573             this.lastHeight = h;
37574             this.el.setHeight(h);
37575             this.fireEvent("autosize", this, h);
37576         }
37577     }
37578 });/*
37579  * Based on:
37580  * Ext JS Library 1.1.1
37581  * Copyright(c) 2006-2007, Ext JS, LLC.
37582  *
37583  * Originally Released Under LGPL - original licence link has changed is not relivant.
37584  *
37585  * Fork - LGPL
37586  * <script type="text/javascript">
37587  */
37588  
37589
37590 /**
37591  * @class Roo.form.NumberField
37592  * @extends Roo.form.TextField
37593  * Numeric text field that provides automatic keystroke filtering and numeric validation.
37594  * @constructor
37595  * Creates a new NumberField
37596  * @param {Object} config Configuration options
37597  */
37598 Roo.form.NumberField = function(config){
37599     Roo.form.NumberField.superclass.constructor.call(this, config);
37600 };
37601
37602 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
37603     /**
37604      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
37605      */
37606     fieldClass: "x-form-field x-form-num-field",
37607     /**
37608      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
37609      */
37610     allowDecimals : true,
37611     /**
37612      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
37613      */
37614     decimalSeparator : ".",
37615     /**
37616      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
37617      */
37618     decimalPrecision : 2,
37619     /**
37620      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
37621      */
37622     allowNegative : true,
37623     /**
37624      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
37625      */
37626     minValue : Number.NEGATIVE_INFINITY,
37627     /**
37628      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
37629      */
37630     maxValue : Number.MAX_VALUE,
37631     /**
37632      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
37633      */
37634     minText : "The minimum value for this field is {0}",
37635     /**
37636      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37637      */
37638     maxText : "The maximum value for this field is {0}",
37639     /**
37640      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
37641      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37642      */
37643     nanText : "{0} is not a valid number",
37644
37645     // private
37646     initEvents : function(){
37647         Roo.form.NumberField.superclass.initEvents.call(this);
37648         var allowed = "0123456789";
37649         if(this.allowDecimals){
37650             allowed += this.decimalSeparator;
37651         }
37652         if(this.allowNegative){
37653             allowed += "-";
37654         }
37655         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37656         var keyPress = function(e){
37657             var k = e.getKey();
37658             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37659                 return;
37660             }
37661             var c = e.getCharCode();
37662             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37663                 e.stopEvent();
37664             }
37665         };
37666         this.el.on("keypress", keyPress, this);
37667     },
37668
37669     // private
37670     validateValue : function(value){
37671         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
37672             return false;
37673         }
37674         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37675              return true;
37676         }
37677         var num = this.parseValue(value);
37678         if(isNaN(num)){
37679             this.markInvalid(String.format(this.nanText, value));
37680             return false;
37681         }
37682         if(num < this.minValue){
37683             this.markInvalid(String.format(this.minText, this.minValue));
37684             return false;
37685         }
37686         if(num > this.maxValue){
37687             this.markInvalid(String.format(this.maxText, this.maxValue));
37688             return false;
37689         }
37690         return true;
37691     },
37692
37693     getValue : function(){
37694         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
37695     },
37696
37697     // private
37698     parseValue : function(value){
37699         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37700         return isNaN(value) ? '' : value;
37701     },
37702
37703     // private
37704     fixPrecision : function(value){
37705         var nan = isNaN(value);
37706         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37707             return nan ? '' : value;
37708         }
37709         return parseFloat(value).toFixed(this.decimalPrecision);
37710     },
37711
37712     setValue : function(v){
37713         v = this.fixPrecision(v);
37714         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
37715     },
37716
37717     // private
37718     decimalPrecisionFcn : function(v){
37719         return Math.floor(v);
37720     },
37721
37722     beforeBlur : function(){
37723         var v = this.parseValue(this.getRawValue());
37724         if(v){
37725             this.setValue(v);
37726         }
37727     }
37728 });/*
37729  * Based on:
37730  * Ext JS Library 1.1.1
37731  * Copyright(c) 2006-2007, Ext JS, LLC.
37732  *
37733  * Originally Released Under LGPL - original licence link has changed is not relivant.
37734  *
37735  * Fork - LGPL
37736  * <script type="text/javascript">
37737  */
37738  
37739 /**
37740  * @class Roo.form.DateField
37741  * @extends Roo.form.TriggerField
37742  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37743 * @constructor
37744 * Create a new DateField
37745 * @param {Object} config
37746  */
37747 Roo.form.DateField = function(config){
37748     Roo.form.DateField.superclass.constructor.call(this, config);
37749     
37750       this.addEvents({
37751          
37752         /**
37753          * @event select
37754          * Fires when a date is selected
37755              * @param {Roo.form.DateField} combo This combo box
37756              * @param {Date} date The date selected
37757              */
37758         'select' : true
37759          
37760     });
37761     
37762     
37763     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37764     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37765     this.ddMatch = null;
37766     if(this.disabledDates){
37767         var dd = this.disabledDates;
37768         var re = "(?:";
37769         for(var i = 0; i < dd.length; i++){
37770             re += dd[i];
37771             if(i != dd.length-1) re += "|";
37772         }
37773         this.ddMatch = new RegExp(re + ")");
37774     }
37775 };
37776
37777 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
37778     /**
37779      * @cfg {String} format
37780      * The default date format string which can be overriden for localization support.  The format must be
37781      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37782      */
37783     format : "m/d/y",
37784     /**
37785      * @cfg {String} altFormats
37786      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37787      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37788      */
37789     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
37790     /**
37791      * @cfg {Array} disabledDays
37792      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37793      */
37794     disabledDays : null,
37795     /**
37796      * @cfg {String} disabledDaysText
37797      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37798      */
37799     disabledDaysText : "Disabled",
37800     /**
37801      * @cfg {Array} disabledDates
37802      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37803      * expression so they are very powerful. Some examples:
37804      * <ul>
37805      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37806      * <li>["03/08", "09/16"] would disable those days for every year</li>
37807      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37808      * <li>["03/../2006"] would disable every day in March 2006</li>
37809      * <li>["^03"] would disable every day in every March</li>
37810      * </ul>
37811      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37812      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37813      */
37814     disabledDates : null,
37815     /**
37816      * @cfg {String} disabledDatesText
37817      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37818      */
37819     disabledDatesText : "Disabled",
37820     /**
37821      * @cfg {Date/String} minValue
37822      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37823      * valid format (defaults to null).
37824      */
37825     minValue : null,
37826     /**
37827      * @cfg {Date/String} maxValue
37828      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37829      * valid format (defaults to null).
37830      */
37831     maxValue : null,
37832     /**
37833      * @cfg {String} minText
37834      * The error text to display when the date in the cell is before minValue (defaults to
37835      * 'The date in this field must be after {minValue}').
37836      */
37837     minText : "The date in this field must be equal to or after {0}",
37838     /**
37839      * @cfg {String} maxText
37840      * The error text to display when the date in the cell is after maxValue (defaults to
37841      * 'The date in this field must be before {maxValue}').
37842      */
37843     maxText : "The date in this field must be equal to or before {0}",
37844     /**
37845      * @cfg {String} invalidText
37846      * The error text to display when the date in the field is invalid (defaults to
37847      * '{value} is not a valid date - it must be in the format {format}').
37848      */
37849     invalidText : "{0} is not a valid date - it must be in the format {1}",
37850     /**
37851      * @cfg {String} triggerClass
37852      * An additional CSS class used to style the trigger button.  The trigger will always get the
37853      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37854      * which displays a calendar icon).
37855      */
37856     triggerClass : 'x-form-date-trigger',
37857     
37858
37859     /**
37860      * @cfg {Boolean} useIso
37861      * if enabled, then the date field will use a hidden field to store the 
37862      * real value as iso formated date. default (false)
37863      */ 
37864     useIso : false,
37865     /**
37866      * @cfg {String/Object} autoCreate
37867      * A DomHelper element spec, or true for a default element spec (defaults to
37868      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37869      */ 
37870     // private
37871     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37872     
37873     // private
37874     hiddenField: false,
37875     
37876     onRender : function(ct, position)
37877     {
37878         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37879         if (this.useIso) {
37880             //this.el.dom.removeAttribute('name'); 
37881             Roo.log("Changing name?");
37882             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
37883             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37884                     'before', true);
37885             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
37886             // prevent input submission
37887             this.hiddenName = this.name;
37888         }
37889             
37890             
37891     },
37892     
37893     // private
37894     validateValue : function(value)
37895     {
37896         value = this.formatDate(value);
37897         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37898             Roo.log('super failed');
37899             return false;
37900         }
37901         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37902              return true;
37903         }
37904         var svalue = value;
37905         value = this.parseDate(value);
37906         if(!value){
37907             Roo.log('parse date failed' + svalue);
37908             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37909             return false;
37910         }
37911         var time = value.getTime();
37912         if(this.minValue && time < this.minValue.getTime()){
37913             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37914             return false;
37915         }
37916         if(this.maxValue && time > this.maxValue.getTime()){
37917             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37918             return false;
37919         }
37920         if(this.disabledDays){
37921             var day = value.getDay();
37922             for(var i = 0; i < this.disabledDays.length; i++) {
37923                 if(day === this.disabledDays[i]){
37924                     this.markInvalid(this.disabledDaysText);
37925                     return false;
37926                 }
37927             }
37928         }
37929         var fvalue = this.formatDate(value);
37930         if(this.ddMatch && this.ddMatch.test(fvalue)){
37931             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37932             return false;
37933         }
37934         return true;
37935     },
37936
37937     // private
37938     // Provides logic to override the default TriggerField.validateBlur which just returns true
37939     validateBlur : function(){
37940         return !this.menu || !this.menu.isVisible();
37941     },
37942     
37943     getName: function()
37944     {
37945         // returns hidden if it's set..
37946         if (!this.rendered) {return ''};
37947         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
37948         
37949     },
37950
37951     /**
37952      * Returns the current date value of the date field.
37953      * @return {Date} The date value
37954      */
37955     getValue : function(){
37956         
37957         return  this.hiddenField ?
37958                 this.hiddenField.value :
37959                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37960     },
37961
37962     /**
37963      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37964      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37965      * (the default format used is "m/d/y").
37966      * <br />Usage:
37967      * <pre><code>
37968 //All of these calls set the same date value (May 4, 2006)
37969
37970 //Pass a date object:
37971 var dt = new Date('5/4/06');
37972 dateField.setValue(dt);
37973
37974 //Pass a date string (default format):
37975 dateField.setValue('5/4/06');
37976
37977 //Pass a date string (custom format):
37978 dateField.format = 'Y-m-d';
37979 dateField.setValue('2006-5-4');
37980 </code></pre>
37981      * @param {String/Date} date The date or valid date string
37982      */
37983     setValue : function(date){
37984         if (this.hiddenField) {
37985             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37986         }
37987         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37988         // make sure the value field is always stored as a date..
37989         this.value = this.parseDate(date);
37990         
37991         
37992     },
37993
37994     // private
37995     parseDate : function(value){
37996         if(!value || value instanceof Date){
37997             return value;
37998         }
37999         var v = Date.parseDate(value, this.format);
38000          if (!v && this.useIso) {
38001             v = Date.parseDate(value, 'Y-m-d');
38002         }
38003         if(!v && this.altFormats){
38004             if(!this.altFormatsArray){
38005                 this.altFormatsArray = this.altFormats.split("|");
38006             }
38007             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38008                 v = Date.parseDate(value, this.altFormatsArray[i]);
38009             }
38010         }
38011         return v;
38012     },
38013
38014     // private
38015     formatDate : function(date, fmt){
38016         return (!date || !(date instanceof Date)) ?
38017                date : date.dateFormat(fmt || this.format);
38018     },
38019
38020     // private
38021     menuListeners : {
38022         select: function(m, d){
38023             
38024             this.setValue(d);
38025             this.fireEvent('select', this, d);
38026         },
38027         show : function(){ // retain focus styling
38028             this.onFocus();
38029         },
38030         hide : function(){
38031             this.focus.defer(10, this);
38032             var ml = this.menuListeners;
38033             this.menu.un("select", ml.select,  this);
38034             this.menu.un("show", ml.show,  this);
38035             this.menu.un("hide", ml.hide,  this);
38036         }
38037     },
38038
38039     // private
38040     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38041     onTriggerClick : function(){
38042         if(this.disabled){
38043             return;
38044         }
38045         if(this.menu == null){
38046             this.menu = new Roo.menu.DateMenu();
38047         }
38048         Roo.apply(this.menu.picker,  {
38049             showClear: this.allowBlank,
38050             minDate : this.minValue,
38051             maxDate : this.maxValue,
38052             disabledDatesRE : this.ddMatch,
38053             disabledDatesText : this.disabledDatesText,
38054             disabledDays : this.disabledDays,
38055             disabledDaysText : this.disabledDaysText,
38056             format : this.useIso ? 'Y-m-d' : this.format,
38057             minText : String.format(this.minText, this.formatDate(this.minValue)),
38058             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38059         });
38060         this.menu.on(Roo.apply({}, this.menuListeners, {
38061             scope:this
38062         }));
38063         this.menu.picker.setValue(this.getValue() || new Date());
38064         this.menu.show(this.el, "tl-bl?");
38065     },
38066
38067     beforeBlur : function(){
38068         var v = this.parseDate(this.getRawValue());
38069         if(v){
38070             this.setValue(v);
38071         }
38072     }
38073
38074     /** @cfg {Boolean} grow @hide */
38075     /** @cfg {Number} growMin @hide */
38076     /** @cfg {Number} growMax @hide */
38077     /**
38078      * @hide
38079      * @method autoSize
38080      */
38081 });/*
38082  * Based on:
38083  * Ext JS Library 1.1.1
38084  * Copyright(c) 2006-2007, Ext JS, LLC.
38085  *
38086  * Originally Released Under LGPL - original licence link has changed is not relivant.
38087  *
38088  * Fork - LGPL
38089  * <script type="text/javascript">
38090  */
38091  
38092 /**
38093  * @class Roo.form.MonthField
38094  * @extends Roo.form.TriggerField
38095  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38096 * @constructor
38097 * Create a new MonthField
38098 * @param {Object} config
38099  */
38100 Roo.form.MonthField = function(config){
38101     
38102     Roo.form.MonthField.superclass.constructor.call(this, config);
38103     
38104       this.addEvents({
38105          
38106         /**
38107          * @event select
38108          * Fires when a date is selected
38109              * @param {Roo.form.MonthFieeld} combo This combo box
38110              * @param {Date} date The date selected
38111              */
38112         'select' : true
38113          
38114     });
38115     
38116     
38117     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38118     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38119     this.ddMatch = null;
38120     if(this.disabledDates){
38121         var dd = this.disabledDates;
38122         var re = "(?:";
38123         for(var i = 0; i < dd.length; i++){
38124             re += dd[i];
38125             if(i != dd.length-1) re += "|";
38126         }
38127         this.ddMatch = new RegExp(re + ")");
38128     }
38129 };
38130
38131 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38132     /**
38133      * @cfg {String} format
38134      * The default date format string which can be overriden for localization support.  The format must be
38135      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38136      */
38137     format : "M Y",
38138     /**
38139      * @cfg {String} altFormats
38140      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38141      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38142      */
38143     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38144     /**
38145      * @cfg {Array} disabledDays
38146      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38147      */
38148     disabledDays : [0,1,2,3,4,5,6],
38149     /**
38150      * @cfg {String} disabledDaysText
38151      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38152      */
38153     disabledDaysText : "Disabled",
38154     /**
38155      * @cfg {Array} disabledDates
38156      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38157      * expression so they are very powerful. Some examples:
38158      * <ul>
38159      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38160      * <li>["03/08", "09/16"] would disable those days for every year</li>
38161      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38162      * <li>["03/../2006"] would disable every day in March 2006</li>
38163      * <li>["^03"] would disable every day in every March</li>
38164      * </ul>
38165      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38166      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38167      */
38168     disabledDates : null,
38169     /**
38170      * @cfg {String} disabledDatesText
38171      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38172      */
38173     disabledDatesText : "Disabled",
38174     /**
38175      * @cfg {Date/String} minValue
38176      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38177      * valid format (defaults to null).
38178      */
38179     minValue : null,
38180     /**
38181      * @cfg {Date/String} maxValue
38182      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38183      * valid format (defaults to null).
38184      */
38185     maxValue : null,
38186     /**
38187      * @cfg {String} minText
38188      * The error text to display when the date in the cell is before minValue (defaults to
38189      * 'The date in this field must be after {minValue}').
38190      */
38191     minText : "The date in this field must be equal to or after {0}",
38192     /**
38193      * @cfg {String} maxTextf
38194      * The error text to display when the date in the cell is after maxValue (defaults to
38195      * 'The date in this field must be before {maxValue}').
38196      */
38197     maxText : "The date in this field must be equal to or before {0}",
38198     /**
38199      * @cfg {String} invalidText
38200      * The error text to display when the date in the field is invalid (defaults to
38201      * '{value} is not a valid date - it must be in the format {format}').
38202      */
38203     invalidText : "{0} is not a valid date - it must be in the format {1}",
38204     /**
38205      * @cfg {String} triggerClass
38206      * An additional CSS class used to style the trigger button.  The trigger will always get the
38207      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38208      * which displays a calendar icon).
38209      */
38210     triggerClass : 'x-form-date-trigger',
38211     
38212
38213     /**
38214      * @cfg {Boolean} useIso
38215      * if enabled, then the date field will use a hidden field to store the 
38216      * real value as iso formated date. default (true)
38217      */ 
38218     useIso : true,
38219     /**
38220      * @cfg {String/Object} autoCreate
38221      * A DomHelper element spec, or true for a default element spec (defaults to
38222      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38223      */ 
38224     // private
38225     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38226     
38227     // private
38228     hiddenField: false,
38229     
38230     hideMonthPicker : false,
38231     
38232     onRender : function(ct, position)
38233     {
38234         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38235         if (this.useIso) {
38236             this.el.dom.removeAttribute('name'); 
38237             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38238                     'before', true);
38239             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38240             // prevent input submission
38241             this.hiddenName = this.name;
38242         }
38243             
38244             
38245     },
38246     
38247     // private
38248     validateValue : function(value)
38249     {
38250         value = this.formatDate(value);
38251         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38252             return false;
38253         }
38254         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38255              return true;
38256         }
38257         var svalue = value;
38258         value = this.parseDate(value);
38259         if(!value){
38260             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38261             return false;
38262         }
38263         var time = value.getTime();
38264         if(this.minValue && time < this.minValue.getTime()){
38265             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38266             return false;
38267         }
38268         if(this.maxValue && time > this.maxValue.getTime()){
38269             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38270             return false;
38271         }
38272         /*if(this.disabledDays){
38273             var day = value.getDay();
38274             for(var i = 0; i < this.disabledDays.length; i++) {
38275                 if(day === this.disabledDays[i]){
38276                     this.markInvalid(this.disabledDaysText);
38277                     return false;
38278                 }
38279             }
38280         }
38281         */
38282         var fvalue = this.formatDate(value);
38283         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38284             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38285             return false;
38286         }
38287         */
38288         return true;
38289     },
38290
38291     // private
38292     // Provides logic to override the default TriggerField.validateBlur which just returns true
38293     validateBlur : function(){
38294         return !this.menu || !this.menu.isVisible();
38295     },
38296
38297     /**
38298      * Returns the current date value of the date field.
38299      * @return {Date} The date value
38300      */
38301     getValue : function(){
38302         
38303         
38304         
38305         return  this.hiddenField ?
38306                 this.hiddenField.value :
38307                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38308     },
38309
38310     /**
38311      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38312      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38313      * (the default format used is "m/d/y").
38314      * <br />Usage:
38315      * <pre><code>
38316 //All of these calls set the same date value (May 4, 2006)
38317
38318 //Pass a date object:
38319 var dt = new Date('5/4/06');
38320 monthField.setValue(dt);
38321
38322 //Pass a date string (default format):
38323 monthField.setValue('5/4/06');
38324
38325 //Pass a date string (custom format):
38326 monthField.format = 'Y-m-d';
38327 monthField.setValue('2006-5-4');
38328 </code></pre>
38329      * @param {String/Date} date The date or valid date string
38330      */
38331     setValue : function(date){
38332         Roo.log('month setValue' + date);
38333         // can only be first of month..
38334         
38335         var val = this.parseDate(date);
38336         
38337         if (this.hiddenField) {
38338             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38339         }
38340         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38341         this.value = this.parseDate(date);
38342     },
38343
38344     // private
38345     parseDate : function(value){
38346         if(!value || value instanceof Date){
38347             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38348             return value;
38349         }
38350         var v = Date.parseDate(value, this.format);
38351         if (!v && this.useIso) {
38352             v = Date.parseDate(value, 'Y-m-d');
38353         }
38354         if (v) {
38355             // 
38356             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38357         }
38358         
38359         
38360         if(!v && this.altFormats){
38361             if(!this.altFormatsArray){
38362                 this.altFormatsArray = this.altFormats.split("|");
38363             }
38364             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38365                 v = Date.parseDate(value, this.altFormatsArray[i]);
38366             }
38367         }
38368         return v;
38369     },
38370
38371     // private
38372     formatDate : function(date, fmt){
38373         return (!date || !(date instanceof Date)) ?
38374                date : date.dateFormat(fmt || this.format);
38375     },
38376
38377     // private
38378     menuListeners : {
38379         select: function(m, d){
38380             this.setValue(d);
38381             this.fireEvent('select', this, d);
38382         },
38383         show : function(){ // retain focus styling
38384             this.onFocus();
38385         },
38386         hide : function(){
38387             this.focus.defer(10, this);
38388             var ml = this.menuListeners;
38389             this.menu.un("select", ml.select,  this);
38390             this.menu.un("show", ml.show,  this);
38391             this.menu.un("hide", ml.hide,  this);
38392         }
38393     },
38394     // private
38395     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38396     onTriggerClick : function(){
38397         if(this.disabled){
38398             return;
38399         }
38400         if(this.menu == null){
38401             this.menu = new Roo.menu.DateMenu();
38402            
38403         }
38404         
38405         Roo.apply(this.menu.picker,  {
38406             
38407             showClear: this.allowBlank,
38408             minDate : this.minValue,
38409             maxDate : this.maxValue,
38410             disabledDatesRE : this.ddMatch,
38411             disabledDatesText : this.disabledDatesText,
38412             
38413             format : this.useIso ? 'Y-m-d' : this.format,
38414             minText : String.format(this.minText, this.formatDate(this.minValue)),
38415             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38416             
38417         });
38418          this.menu.on(Roo.apply({}, this.menuListeners, {
38419             scope:this
38420         }));
38421        
38422         
38423         var m = this.menu;
38424         var p = m.picker;
38425         
38426         // hide month picker get's called when we called by 'before hide';
38427         
38428         var ignorehide = true;
38429         p.hideMonthPicker  = function(disableAnim){
38430             if (ignorehide) {
38431                 return;
38432             }
38433              if(this.monthPicker){
38434                 Roo.log("hideMonthPicker called");
38435                 if(disableAnim === true){
38436                     this.monthPicker.hide();
38437                 }else{
38438                     this.monthPicker.slideOut('t', {duration:.2});
38439                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38440                     p.fireEvent("select", this, this.value);
38441                     m.hide();
38442                 }
38443             }
38444         }
38445         
38446         Roo.log('picker set value');
38447         Roo.log(this.getValue());
38448         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
38449         m.show(this.el, 'tl-bl?');
38450         ignorehide  = false;
38451         // this will trigger hideMonthPicker..
38452         
38453         
38454         // hidden the day picker
38455         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
38456         
38457         
38458         
38459       
38460         
38461         p.showMonthPicker.defer(100, p);
38462     
38463         
38464        
38465     },
38466
38467     beforeBlur : function(){
38468         var v = this.parseDate(this.getRawValue());
38469         if(v){
38470             this.setValue(v);
38471         }
38472     }
38473
38474     /** @cfg {Boolean} grow @hide */
38475     /** @cfg {Number} growMin @hide */
38476     /** @cfg {Number} growMax @hide */
38477     /**
38478      * @hide
38479      * @method autoSize
38480      */
38481 });/*
38482  * Based on:
38483  * Ext JS Library 1.1.1
38484  * Copyright(c) 2006-2007, Ext JS, LLC.
38485  *
38486  * Originally Released Under LGPL - original licence link has changed is not relivant.
38487  *
38488  * Fork - LGPL
38489  * <script type="text/javascript">
38490  */
38491  
38492
38493 /**
38494  * @class Roo.form.ComboBox
38495  * @extends Roo.form.TriggerField
38496  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
38497  * @constructor
38498  * Create a new ComboBox.
38499  * @param {Object} config Configuration options
38500  */
38501 Roo.form.ComboBox = function(config){
38502     Roo.form.ComboBox.superclass.constructor.call(this, config);
38503     this.addEvents({
38504         /**
38505          * @event expand
38506          * Fires when the dropdown list is expanded
38507              * @param {Roo.form.ComboBox} combo This combo box
38508              */
38509         'expand' : true,
38510         /**
38511          * @event collapse
38512          * Fires when the dropdown list is collapsed
38513              * @param {Roo.form.ComboBox} combo This combo box
38514              */
38515         'collapse' : true,
38516         /**
38517          * @event beforeselect
38518          * Fires before a list item is selected. Return false to cancel the selection.
38519              * @param {Roo.form.ComboBox} combo This combo box
38520              * @param {Roo.data.Record} record The data record returned from the underlying store
38521              * @param {Number} index The index of the selected item in the dropdown list
38522              */
38523         'beforeselect' : true,
38524         /**
38525          * @event select
38526          * Fires when a list item is selected
38527              * @param {Roo.form.ComboBox} combo This combo box
38528              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
38529              * @param {Number} index The index of the selected item in the dropdown list
38530              */
38531         'select' : true,
38532         /**
38533          * @event beforequery
38534          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
38535          * The event object passed has these properties:
38536              * @param {Roo.form.ComboBox} combo This combo box
38537              * @param {String} query The query
38538              * @param {Boolean} forceAll true to force "all" query
38539              * @param {Boolean} cancel true to cancel the query
38540              * @param {Object} e The query event object
38541              */
38542         'beforequery': true,
38543          /**
38544          * @event add
38545          * Fires when the 'add' icon is pressed (add a listener to enable add button)
38546              * @param {Roo.form.ComboBox} combo This combo box
38547              */
38548         'add' : true,
38549         /**
38550          * @event edit
38551          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
38552              * @param {Roo.form.ComboBox} combo This combo box
38553              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
38554              */
38555         'edit' : true
38556         
38557         
38558     });
38559     if(this.transform){
38560         this.allowDomMove = false;
38561         var s = Roo.getDom(this.transform);
38562         if(!this.hiddenName){
38563             this.hiddenName = s.name;
38564         }
38565         if(!this.store){
38566             this.mode = 'local';
38567             var d = [], opts = s.options;
38568             for(var i = 0, len = opts.length;i < len; i++){
38569                 var o = opts[i];
38570                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
38571                 if(o.selected) {
38572                     this.value = value;
38573                 }
38574                 d.push([value, o.text]);
38575             }
38576             this.store = new Roo.data.SimpleStore({
38577                 'id': 0,
38578                 fields: ['value', 'text'],
38579                 data : d
38580             });
38581             this.valueField = 'value';
38582             this.displayField = 'text';
38583         }
38584         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
38585         if(!this.lazyRender){
38586             this.target = true;
38587             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
38588             s.parentNode.removeChild(s); // remove it
38589             this.render(this.el.parentNode);
38590         }else{
38591             s.parentNode.removeChild(s); // remove it
38592         }
38593
38594     }
38595     if (this.store) {
38596         this.store = Roo.factory(this.store, Roo.data);
38597     }
38598     
38599     this.selectedIndex = -1;
38600     if(this.mode == 'local'){
38601         if(config.queryDelay === undefined){
38602             this.queryDelay = 10;
38603         }
38604         if(config.minChars === undefined){
38605             this.minChars = 0;
38606         }
38607     }
38608 };
38609
38610 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
38611     /**
38612      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
38613      */
38614     /**
38615      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
38616      * rendering into an Roo.Editor, defaults to false)
38617      */
38618     /**
38619      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
38620      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
38621      */
38622     /**
38623      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
38624      */
38625     /**
38626      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
38627      * the dropdown list (defaults to undefined, with no header element)
38628      */
38629
38630      /**
38631      * @cfg {String/Roo.Template} tpl The template to use to render the output
38632      */
38633      
38634     // private
38635     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
38636     /**
38637      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
38638      */
38639     listWidth: undefined,
38640     /**
38641      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
38642      * mode = 'remote' or 'text' if mode = 'local')
38643      */
38644     displayField: undefined,
38645     /**
38646      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
38647      * mode = 'remote' or 'value' if mode = 'local'). 
38648      * Note: use of a valueField requires the user make a selection
38649      * in order for a value to be mapped.
38650      */
38651     valueField: undefined,
38652     
38653     
38654     /**
38655      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
38656      * field's data value (defaults to the underlying DOM element's name)
38657      */
38658     hiddenName: undefined,
38659     /**
38660      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
38661      */
38662     listClass: '',
38663     /**
38664      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
38665      */
38666     selectedClass: 'x-combo-selected',
38667     /**
38668      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38669      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
38670      * which displays a downward arrow icon).
38671      */
38672     triggerClass : 'x-form-arrow-trigger',
38673     /**
38674      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
38675      */
38676     shadow:'sides',
38677     /**
38678      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
38679      * anchor positions (defaults to 'tl-bl')
38680      */
38681     listAlign: 'tl-bl?',
38682     /**
38683      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
38684      */
38685     maxHeight: 300,
38686     /**
38687      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
38688      * query specified by the allQuery config option (defaults to 'query')
38689      */
38690     triggerAction: 'query',
38691     /**
38692      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
38693      * (defaults to 4, does not apply if editable = false)
38694      */
38695     minChars : 4,
38696     /**
38697      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
38698      * delay (typeAheadDelay) if it matches a known value (defaults to false)
38699      */
38700     typeAhead: false,
38701     /**
38702      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
38703      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
38704      */
38705     queryDelay: 500,
38706     /**
38707      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
38708      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
38709      */
38710     pageSize: 0,
38711     /**
38712      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
38713      * when editable = true (defaults to false)
38714      */
38715     selectOnFocus:false,
38716     /**
38717      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
38718      */
38719     queryParam: 'query',
38720     /**
38721      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
38722      * when mode = 'remote' (defaults to 'Loading...')
38723      */
38724     loadingText: 'Loading...',
38725     /**
38726      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
38727      */
38728     resizable: false,
38729     /**
38730      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
38731      */
38732     handleHeight : 8,
38733     /**
38734      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
38735      * traditional select (defaults to true)
38736      */
38737     editable: true,
38738     /**
38739      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
38740      */
38741     allQuery: '',
38742     /**
38743      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
38744      */
38745     mode: 'remote',
38746     /**
38747      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
38748      * listWidth has a higher value)
38749      */
38750     minListWidth : 70,
38751     /**
38752      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
38753      * allow the user to set arbitrary text into the field (defaults to false)
38754      */
38755     forceSelection:false,
38756     /**
38757      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
38758      * if typeAhead = true (defaults to 250)
38759      */
38760     typeAheadDelay : 250,
38761     /**
38762      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
38763      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
38764      */
38765     valueNotFoundText : undefined,
38766     /**
38767      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
38768      */
38769     blockFocus : false,
38770     
38771     /**
38772      * @cfg {Boolean} disableClear Disable showing of clear button.
38773      */
38774     disableClear : false,
38775     /**
38776      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
38777      */
38778     alwaysQuery : false,
38779     
38780     //private
38781     addicon : false,
38782     editicon: false,
38783     
38784     // element that contains real text value.. (when hidden is used..)
38785      
38786     // private
38787     onRender : function(ct, position){
38788         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
38789         if(this.hiddenName){
38790             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
38791                     'before', true);
38792             this.hiddenField.value =
38793                 this.hiddenValue !== undefined ? this.hiddenValue :
38794                 this.value !== undefined ? this.value : '';
38795
38796             // prevent input submission
38797             this.el.dom.removeAttribute('name');
38798              
38799              
38800         }
38801         if(Roo.isGecko){
38802             this.el.dom.setAttribute('autocomplete', 'off');
38803         }
38804
38805         var cls = 'x-combo-list';
38806
38807         this.list = new Roo.Layer({
38808             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
38809         });
38810
38811         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
38812         this.list.setWidth(lw);
38813         this.list.swallowEvent('mousewheel');
38814         this.assetHeight = 0;
38815
38816         if(this.title){
38817             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
38818             this.assetHeight += this.header.getHeight();
38819         }
38820
38821         this.innerList = this.list.createChild({cls:cls+'-inner'});
38822         this.innerList.on('mouseover', this.onViewOver, this);
38823         this.innerList.on('mousemove', this.onViewMove, this);
38824         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38825         
38826         if(this.allowBlank && !this.pageSize && !this.disableClear){
38827             this.footer = this.list.createChild({cls:cls+'-ft'});
38828             this.pageTb = new Roo.Toolbar(this.footer);
38829            
38830         }
38831         if(this.pageSize){
38832             this.footer = this.list.createChild({cls:cls+'-ft'});
38833             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
38834                     {pageSize: this.pageSize});
38835             
38836         }
38837         
38838         if (this.pageTb && this.allowBlank && !this.disableClear) {
38839             var _this = this;
38840             this.pageTb.add(new Roo.Toolbar.Fill(), {
38841                 cls: 'x-btn-icon x-btn-clear',
38842                 text: '&#160;',
38843                 handler: function()
38844                 {
38845                     _this.collapse();
38846                     _this.clearValue();
38847                     _this.onSelect(false, -1);
38848                 }
38849             });
38850         }
38851         if (this.footer) {
38852             this.assetHeight += this.footer.getHeight();
38853         }
38854         
38855
38856         if(!this.tpl){
38857             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
38858         }
38859
38860         this.view = new Roo.View(this.innerList, this.tpl, {
38861             singleSelect:true, store: this.store, selectedClass: this.selectedClass
38862         });
38863
38864         this.view.on('click', this.onViewClick, this);
38865
38866         this.store.on('beforeload', this.onBeforeLoad, this);
38867         this.store.on('load', this.onLoad, this);
38868         this.store.on('loadexception', this.onLoadException, this);
38869
38870         if(this.resizable){
38871             this.resizer = new Roo.Resizable(this.list,  {
38872                pinned:true, handles:'se'
38873             });
38874             this.resizer.on('resize', function(r, w, h){
38875                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
38876                 this.listWidth = w;
38877                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
38878                 this.restrictHeight();
38879             }, this);
38880             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
38881         }
38882         if(!this.editable){
38883             this.editable = true;
38884             this.setEditable(false);
38885         }  
38886         
38887         
38888         if (typeof(this.events.add.listeners) != 'undefined') {
38889             
38890             this.addicon = this.wrap.createChild(
38891                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
38892        
38893             this.addicon.on('click', function(e) {
38894                 this.fireEvent('add', this);
38895             }, this);
38896         }
38897         if (typeof(this.events.edit.listeners) != 'undefined') {
38898             
38899             this.editicon = this.wrap.createChild(
38900                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
38901             if (this.addicon) {
38902                 this.editicon.setStyle('margin-left', '40px');
38903             }
38904             this.editicon.on('click', function(e) {
38905                 
38906                 // we fire even  if inothing is selected..
38907                 this.fireEvent('edit', this, this.lastData );
38908                 
38909             }, this);
38910         }
38911         
38912         
38913         
38914     },
38915
38916     // private
38917     initEvents : function(){
38918         Roo.form.ComboBox.superclass.initEvents.call(this);
38919
38920         this.keyNav = new Roo.KeyNav(this.el, {
38921             "up" : function(e){
38922                 this.inKeyMode = true;
38923                 this.selectPrev();
38924             },
38925
38926             "down" : function(e){
38927                 if(!this.isExpanded()){
38928                     this.onTriggerClick();
38929                 }else{
38930                     this.inKeyMode = true;
38931                     this.selectNext();
38932                 }
38933             },
38934
38935             "enter" : function(e){
38936                 this.onViewClick();
38937                 //return true;
38938             },
38939
38940             "esc" : function(e){
38941                 this.collapse();
38942             },
38943
38944             "tab" : function(e){
38945                 this.onViewClick(false);
38946                 this.fireEvent("specialkey", this, e);
38947                 return true;
38948             },
38949
38950             scope : this,
38951
38952             doRelay : function(foo, bar, hname){
38953                 if(hname == 'down' || this.scope.isExpanded()){
38954                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38955                 }
38956                 return true;
38957             },
38958
38959             forceKeyDown: true
38960         });
38961         this.queryDelay = Math.max(this.queryDelay || 10,
38962                 this.mode == 'local' ? 10 : 250);
38963         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
38964         if(this.typeAhead){
38965             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
38966         }
38967         if(this.editable !== false){
38968             this.el.on("keyup", this.onKeyUp, this);
38969         }
38970         if(this.forceSelection){
38971             this.on('blur', this.doForce, this);
38972         }
38973     },
38974
38975     onDestroy : function(){
38976         if(this.view){
38977             this.view.setStore(null);
38978             this.view.el.removeAllListeners();
38979             this.view.el.remove();
38980             this.view.purgeListeners();
38981         }
38982         if(this.list){
38983             this.list.destroy();
38984         }
38985         if(this.store){
38986             this.store.un('beforeload', this.onBeforeLoad, this);
38987             this.store.un('load', this.onLoad, this);
38988             this.store.un('loadexception', this.onLoadException, this);
38989         }
38990         Roo.form.ComboBox.superclass.onDestroy.call(this);
38991     },
38992
38993     // private
38994     fireKey : function(e){
38995         if(e.isNavKeyPress() && !this.list.isVisible()){
38996             this.fireEvent("specialkey", this, e);
38997         }
38998     },
38999
39000     // private
39001     onResize: function(w, h){
39002         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39003         
39004         if(typeof w != 'number'){
39005             // we do not handle it!?!?
39006             return;
39007         }
39008         var tw = this.trigger.getWidth();
39009         tw += this.addicon ? this.addicon.getWidth() : 0;
39010         tw += this.editicon ? this.editicon.getWidth() : 0;
39011         var x = w - tw;
39012         this.el.setWidth( this.adjustWidth('input', x));
39013             
39014         this.trigger.setStyle('left', x+'px');
39015         
39016         if(this.list && this.listWidth === undefined){
39017             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39018             this.list.setWidth(lw);
39019             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39020         }
39021         
39022     
39023         
39024     },
39025
39026     /**
39027      * Allow or prevent the user from directly editing the field text.  If false is passed,
39028      * the user will only be able to select from the items defined in the dropdown list.  This method
39029      * is the runtime equivalent of setting the 'editable' config option at config time.
39030      * @param {Boolean} value True to allow the user to directly edit the field text
39031      */
39032     setEditable : function(value){
39033         if(value == this.editable){
39034             return;
39035         }
39036         this.editable = value;
39037         if(!value){
39038             this.el.dom.setAttribute('readOnly', true);
39039             this.el.on('mousedown', this.onTriggerClick,  this);
39040             this.el.addClass('x-combo-noedit');
39041         }else{
39042             this.el.dom.setAttribute('readOnly', false);
39043             this.el.un('mousedown', this.onTriggerClick,  this);
39044             this.el.removeClass('x-combo-noedit');
39045         }
39046     },
39047
39048     // private
39049     onBeforeLoad : function(){
39050         if(!this.hasFocus){
39051             return;
39052         }
39053         this.innerList.update(this.loadingText ?
39054                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39055         this.restrictHeight();
39056         this.selectedIndex = -1;
39057     },
39058
39059     // private
39060     onLoad : function(){
39061         if(!this.hasFocus){
39062             return;
39063         }
39064         if(this.store.getCount() > 0){
39065             this.expand();
39066             this.restrictHeight();
39067             if(this.lastQuery == this.allQuery){
39068                 if(this.editable){
39069                     this.el.dom.select();
39070                 }
39071                 if(!this.selectByValue(this.value, true)){
39072                     this.select(0, true);
39073                 }
39074             }else{
39075                 this.selectNext();
39076                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39077                     this.taTask.delay(this.typeAheadDelay);
39078                 }
39079             }
39080         }else{
39081             this.onEmptyResults();
39082         }
39083         //this.el.focus();
39084     },
39085     // private
39086     onLoadException : function()
39087     {
39088         this.collapse();
39089         Roo.log(this.store.reader.jsonData);
39090         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39091             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39092         }
39093         
39094         
39095     },
39096     // private
39097     onTypeAhead : function(){
39098         if(this.store.getCount() > 0){
39099             var r = this.store.getAt(0);
39100             var newValue = r.data[this.displayField];
39101             var len = newValue.length;
39102             var selStart = this.getRawValue().length;
39103             if(selStart != len){
39104                 this.setRawValue(newValue);
39105                 this.selectText(selStart, newValue.length);
39106             }
39107         }
39108     },
39109
39110     // private
39111     onSelect : function(record, index){
39112         if(this.fireEvent('beforeselect', this, record, index) !== false){
39113             this.setFromData(index > -1 ? record.data : false);
39114             this.collapse();
39115             this.fireEvent('select', this, record, index);
39116         }
39117     },
39118
39119     /**
39120      * Returns the currently selected field value or empty string if no value is set.
39121      * @return {String} value The selected value
39122      */
39123     getValue : function(){
39124         if(this.valueField){
39125             return typeof this.value != 'undefined' ? this.value : '';
39126         }else{
39127             return Roo.form.ComboBox.superclass.getValue.call(this);
39128         }
39129     },
39130
39131     /**
39132      * Clears any text/value currently set in the field
39133      */
39134     clearValue : function(){
39135         if(this.hiddenField){
39136             this.hiddenField.value = '';
39137         }
39138         this.value = '';
39139         this.setRawValue('');
39140         this.lastSelectionText = '';
39141         
39142     },
39143
39144     /**
39145      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39146      * will be displayed in the field.  If the value does not match the data value of an existing item,
39147      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39148      * Otherwise the field will be blank (although the value will still be set).
39149      * @param {String} value The value to match
39150      */
39151     setValue : function(v){
39152         var text = v;
39153         if(this.valueField){
39154             var r = this.findRecord(this.valueField, v);
39155             if(r){
39156                 text = r.data[this.displayField];
39157             }else if(this.valueNotFoundText !== undefined){
39158                 text = this.valueNotFoundText;
39159             }
39160         }
39161         this.lastSelectionText = text;
39162         if(this.hiddenField){
39163             this.hiddenField.value = v;
39164         }
39165         Roo.form.ComboBox.superclass.setValue.call(this, text);
39166         this.value = v;
39167     },
39168     /**
39169      * @property {Object} the last set data for the element
39170      */
39171     
39172     lastData : false,
39173     /**
39174      * Sets the value of the field based on a object which is related to the record format for the store.
39175      * @param {Object} value the value to set as. or false on reset?
39176      */
39177     setFromData : function(o){
39178         var dv = ''; // display value
39179         var vv = ''; // value value..
39180         this.lastData = o;
39181         if (this.displayField) {
39182             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39183         } else {
39184             // this is an error condition!!!
39185             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39186         }
39187         
39188         if(this.valueField){
39189             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39190         }
39191         if(this.hiddenField){
39192             this.hiddenField.value = vv;
39193             
39194             this.lastSelectionText = dv;
39195             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39196             this.value = vv;
39197             return;
39198         }
39199         // no hidden field.. - we store the value in 'value', but still display
39200         // display field!!!!
39201         this.lastSelectionText = dv;
39202         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39203         this.value = vv;
39204         
39205         
39206     },
39207     // private
39208     reset : function(){
39209         // overridden so that last data is reset..
39210         this.setValue(this.originalValue);
39211         this.clearInvalid();
39212         this.lastData = false;
39213         if (this.view) {
39214             this.view.clearSelections();
39215         }
39216     },
39217     // private
39218     findRecord : function(prop, value){
39219         var record;
39220         if(this.store.getCount() > 0){
39221             this.store.each(function(r){
39222                 if(r.data[prop] == value){
39223                     record = r;
39224                     return false;
39225                 }
39226                 return true;
39227             });
39228         }
39229         return record;
39230     },
39231     
39232     getName: function()
39233     {
39234         // returns hidden if it's set..
39235         if (!this.rendered) {return ''};
39236         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39237         
39238     },
39239     // private
39240     onViewMove : function(e, t){
39241         this.inKeyMode = false;
39242     },
39243
39244     // private
39245     onViewOver : function(e, t){
39246         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39247             return;
39248         }
39249         var item = this.view.findItemFromChild(t);
39250         if(item){
39251             var index = this.view.indexOf(item);
39252             this.select(index, false);
39253         }
39254     },
39255
39256     // private
39257     onViewClick : function(doFocus)
39258     {
39259         var index = this.view.getSelectedIndexes()[0];
39260         var r = this.store.getAt(index);
39261         if(r){
39262             this.onSelect(r, index);
39263         }
39264         if(doFocus !== false && !this.blockFocus){
39265             this.el.focus();
39266         }
39267     },
39268
39269     // private
39270     restrictHeight : function(){
39271         this.innerList.dom.style.height = '';
39272         var inner = this.innerList.dom;
39273         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39274         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39275         this.list.beginUpdate();
39276         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39277         this.list.alignTo(this.el, this.listAlign);
39278         this.list.endUpdate();
39279     },
39280
39281     // private
39282     onEmptyResults : function(){
39283         this.collapse();
39284     },
39285
39286     /**
39287      * Returns true if the dropdown list is expanded, else false.
39288      */
39289     isExpanded : function(){
39290         return this.list.isVisible();
39291     },
39292
39293     /**
39294      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39295      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39296      * @param {String} value The data value of the item to select
39297      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39298      * selected item if it is not currently in view (defaults to true)
39299      * @return {Boolean} True if the value matched an item in the list, else false
39300      */
39301     selectByValue : function(v, scrollIntoView){
39302         if(v !== undefined && v !== null){
39303             var r = this.findRecord(this.valueField || this.displayField, v);
39304             if(r){
39305                 this.select(this.store.indexOf(r), scrollIntoView);
39306                 return true;
39307             }
39308         }
39309         return false;
39310     },
39311
39312     /**
39313      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39314      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39315      * @param {Number} index The zero-based index of the list item to select
39316      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39317      * selected item if it is not currently in view (defaults to true)
39318      */
39319     select : function(index, scrollIntoView){
39320         this.selectedIndex = index;
39321         this.view.select(index);
39322         if(scrollIntoView !== false){
39323             var el = this.view.getNode(index);
39324             if(el){
39325                 this.innerList.scrollChildIntoView(el, false);
39326             }
39327         }
39328     },
39329
39330     // private
39331     selectNext : function(){
39332         var ct = this.store.getCount();
39333         if(ct > 0){
39334             if(this.selectedIndex == -1){
39335                 this.select(0);
39336             }else if(this.selectedIndex < ct-1){
39337                 this.select(this.selectedIndex+1);
39338             }
39339         }
39340     },
39341
39342     // private
39343     selectPrev : function(){
39344         var ct = this.store.getCount();
39345         if(ct > 0){
39346             if(this.selectedIndex == -1){
39347                 this.select(0);
39348             }else if(this.selectedIndex != 0){
39349                 this.select(this.selectedIndex-1);
39350             }
39351         }
39352     },
39353
39354     // private
39355     onKeyUp : function(e){
39356         if(this.editable !== false && !e.isSpecialKey()){
39357             this.lastKey = e.getKey();
39358             this.dqTask.delay(this.queryDelay);
39359         }
39360     },
39361
39362     // private
39363     validateBlur : function(){
39364         return !this.list || !this.list.isVisible();   
39365     },
39366
39367     // private
39368     initQuery : function(){
39369         this.doQuery(this.getRawValue());
39370     },
39371
39372     // private
39373     doForce : function(){
39374         if(this.el.dom.value.length > 0){
39375             this.el.dom.value =
39376                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39377              
39378         }
39379     },
39380
39381     /**
39382      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39383      * query allowing the query action to be canceled if needed.
39384      * @param {String} query The SQL query to execute
39385      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39386      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39387      * saved in the current store (defaults to false)
39388      */
39389     doQuery : function(q, forceAll){
39390         if(q === undefined || q === null){
39391             q = '';
39392         }
39393         var qe = {
39394             query: q,
39395             forceAll: forceAll,
39396             combo: this,
39397             cancel:false
39398         };
39399         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39400             return false;
39401         }
39402         q = qe.query;
39403         forceAll = qe.forceAll;
39404         if(forceAll === true || (q.length >= this.minChars)){
39405             if(this.lastQuery != q || this.alwaysQuery){
39406                 this.lastQuery = q;
39407                 if(this.mode == 'local'){
39408                     this.selectedIndex = -1;
39409                     if(forceAll){
39410                         this.store.clearFilter();
39411                     }else{
39412                         this.store.filter(this.displayField, q);
39413                     }
39414                     this.onLoad();
39415                 }else{
39416                     this.store.baseParams[this.queryParam] = q;
39417                     this.store.load({
39418                         params: this.getParams(q)
39419                     });
39420                     this.expand();
39421                 }
39422             }else{
39423                 this.selectedIndex = -1;
39424                 this.onLoad();   
39425             }
39426         }
39427     },
39428
39429     // private
39430     getParams : function(q){
39431         var p = {};
39432         //p[this.queryParam] = q;
39433         if(this.pageSize){
39434             p.start = 0;
39435             p.limit = this.pageSize;
39436         }
39437         return p;
39438     },
39439
39440     /**
39441      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39442      */
39443     collapse : function(){
39444         if(!this.isExpanded()){
39445             return;
39446         }
39447         this.list.hide();
39448         Roo.get(document).un('mousedown', this.collapseIf, this);
39449         Roo.get(document).un('mousewheel', this.collapseIf, this);
39450         if (!this.editable) {
39451             Roo.get(document).un('keydown', this.listKeyPress, this);
39452         }
39453         this.fireEvent('collapse', this);
39454     },
39455
39456     // private
39457     collapseIf : function(e){
39458         if(!e.within(this.wrap) && !e.within(this.list)){
39459             this.collapse();
39460         }
39461     },
39462
39463     /**
39464      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39465      */
39466     expand : function(){
39467         if(this.isExpanded() || !this.hasFocus){
39468             return;
39469         }
39470         this.list.alignTo(this.el, this.listAlign);
39471         this.list.show();
39472         Roo.get(document).on('mousedown', this.collapseIf, this);
39473         Roo.get(document).on('mousewheel', this.collapseIf, this);
39474         if (!this.editable) {
39475             Roo.get(document).on('keydown', this.listKeyPress, this);
39476         }
39477         
39478         this.fireEvent('expand', this);
39479     },
39480
39481     // private
39482     // Implements the default empty TriggerField.onTriggerClick function
39483     onTriggerClick : function(){
39484         if(this.disabled){
39485             return;
39486         }
39487         if(this.isExpanded()){
39488             this.collapse();
39489             if (!this.blockFocus) {
39490                 this.el.focus();
39491             }
39492             
39493         }else {
39494             this.hasFocus = true;
39495             if(this.triggerAction == 'all') {
39496                 this.doQuery(this.allQuery, true);
39497             } else {
39498                 this.doQuery(this.getRawValue());
39499             }
39500             if (!this.blockFocus) {
39501                 this.el.focus();
39502             }
39503         }
39504     },
39505     listKeyPress : function(e)
39506     {
39507         //Roo.log('listkeypress');
39508         // scroll to first matching element based on key pres..
39509         if (e.isSpecialKey()) {
39510             return false;
39511         }
39512         var k = String.fromCharCode(e.getKey()).toUpperCase();
39513         //Roo.log(k);
39514         var match  = false;
39515         var csel = this.view.getSelectedNodes();
39516         var cselitem = false;
39517         if (csel.length) {
39518             var ix = this.view.indexOf(csel[0]);
39519             cselitem  = this.store.getAt(ix);
39520             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
39521                 cselitem = false;
39522             }
39523             
39524         }
39525         
39526         this.store.each(function(v) { 
39527             if (cselitem) {
39528                 // start at existing selection.
39529                 if (cselitem.id == v.id) {
39530                     cselitem = false;
39531                 }
39532                 return;
39533             }
39534                 
39535             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
39536                 match = this.store.indexOf(v);
39537                 return false;
39538             }
39539         }, this);
39540         
39541         if (match === false) {
39542             return true; // no more action?
39543         }
39544         // scroll to?
39545         this.view.select(match);
39546         var sn = Roo.get(this.view.getSelectedNodes()[0])
39547         sn.scrollIntoView(sn.dom.parentNode, false);
39548     }
39549
39550     /** 
39551     * @cfg {Boolean} grow 
39552     * @hide 
39553     */
39554     /** 
39555     * @cfg {Number} growMin 
39556     * @hide 
39557     */
39558     /** 
39559     * @cfg {Number} growMax 
39560     * @hide 
39561     */
39562     /**
39563      * @hide
39564      * @method autoSize
39565      */
39566 });/*
39567  * Copyright(c) 2010-2012, Roo J Solutions Limited
39568  *
39569  * Licence LGPL
39570  *
39571  */
39572
39573 /**
39574  * @class Roo.form.ComboBoxArray
39575  * @extends Roo.form.TextField
39576  * A facebook style adder... for lists of email / people / countries  etc...
39577  * pick multiple items from a combo box, and shows each one.
39578  *
39579  *  Fred [x]  Brian [x]  [Pick another |v]
39580  *
39581  *
39582  *  For this to work: it needs various extra information
39583  *    - normal combo problay has
39584  *      name, hiddenName
39585  *    + displayField, valueField
39586  *
39587  *    For our purpose...
39588  *
39589  *
39590  *   If we change from 'extends' to wrapping...
39591  *   
39592  *  
39593  *
39594  
39595  
39596  * @constructor
39597  * Create a new ComboBoxArray.
39598  * @param {Object} config Configuration options
39599  */
39600  
39601
39602 Roo.form.ComboBoxArray = function(config)
39603 {
39604     
39605     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
39606     
39607     this.items = new Roo.util.MixedCollection(false);
39608     
39609     // construct the child combo...
39610     
39611     
39612     
39613     
39614    
39615     
39616 }
39617
39618  
39619 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
39620
39621     /**
39622      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
39623      */
39624     
39625     lastData : false,
39626     
39627     // behavies liek a hiddne field
39628     inputType:      'hidden',
39629     /**
39630      * @cfg {Number} width The width of the box that displays the selected element
39631      */ 
39632     width:          300,
39633
39634     
39635     
39636     /**
39637      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
39638      */
39639     name : false,
39640     /**
39641      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
39642      */
39643     hiddenName : false,
39644     
39645     
39646     // private the array of items that are displayed..
39647     items  : false,
39648     // private - the hidden field el.
39649     hiddenEl : false,
39650     // private - the filed el..
39651     el : false,
39652     
39653     //validateValue : function() { return true; }, // all values are ok!
39654     //onAddClick: function() { },
39655     
39656     onRender : function(ct, position) 
39657     {
39658         
39659         // create the standard hidden element
39660         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
39661         
39662         
39663         // give fake names to child combo;
39664         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
39665         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
39666         
39667         this.combo = Roo.factory(this.combo, Roo.form);
39668         this.combo.onRender(ct, position);
39669         if (typeof(this.combo.width) != 'undefined') {
39670             this.combo.onResize(this.combo.width,0);
39671         }
39672         
39673         this.combo.initEvents();
39674         
39675         // assigned so form know we need to do this..
39676         this.store          = this.combo.store;
39677         this.valueField     = this.combo.valueField;
39678         this.displayField   = this.combo.displayField ;
39679         
39680         
39681         this.combo.wrap.addClass('x-cbarray-grp');
39682         
39683         var cbwrap = this.combo.wrap.createChild(
39684             {tag: 'div', cls: 'x-cbarray-cb'},
39685             this.combo.el.dom
39686         );
39687         
39688              
39689         this.hiddenEl = this.combo.wrap.createChild({
39690             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
39691         });
39692         this.el = this.combo.wrap.createChild({
39693             tag: 'input',  type:'hidden' , name: this.name, value : ''
39694         });
39695          //   this.el.dom.removeAttribute("name");
39696         
39697         
39698         this.outerWrap = this.combo.wrap;
39699         this.wrap = cbwrap;
39700         
39701         this.outerWrap.setWidth(this.width);
39702         this.outerWrap.dom.removeChild(this.el.dom);
39703         
39704         this.wrap.dom.appendChild(this.el.dom);
39705         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
39706         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
39707         
39708         this.combo.trigger.setStyle('position','relative');
39709         this.combo.trigger.setStyle('left', '0px');
39710         this.combo.trigger.setStyle('top', '2px');
39711         
39712         this.combo.el.setStyle('vertical-align', 'text-bottom');
39713         
39714         //this.trigger.setStyle('vertical-align', 'top');
39715         
39716         // this should use the code from combo really... on('add' ....)
39717         if (this.adder) {
39718             
39719         
39720             this.adder = this.outerWrap.createChild(
39721                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
39722             var _t = this;
39723             this.adder.on('click', function(e) {
39724                 _t.fireEvent('adderclick', this, e);
39725             }, _t);
39726         }
39727         //var _t = this;
39728         //this.adder.on('click', this.onAddClick, _t);
39729         
39730         
39731         this.combo.on('select', function(cb, rec, ix) {
39732             this.addItem(rec.data);
39733             
39734             cb.setValue('');
39735             cb.el.dom.value = '';
39736             //cb.lastData = rec.data;
39737             // add to list
39738             
39739         }, this);
39740         
39741         
39742     },
39743     
39744     
39745     getName: function()
39746     {
39747         // returns hidden if it's set..
39748         if (!this.rendered) {return ''};
39749         return  this.hiddenName ? this.hiddenName : this.name;
39750         
39751     },
39752     
39753     
39754     onResize: function(w, h){
39755         
39756         return;
39757         // not sure if this is needed..
39758         //this.combo.onResize(w,h);
39759         
39760         if(typeof w != 'number'){
39761             // we do not handle it!?!?
39762             return;
39763         }
39764         var tw = this.combo.trigger.getWidth();
39765         tw += this.addicon ? this.addicon.getWidth() : 0;
39766         tw += this.editicon ? this.editicon.getWidth() : 0;
39767         var x = w - tw;
39768         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
39769             
39770         this.combo.trigger.setStyle('left', '0px');
39771         
39772         if(this.list && this.listWidth === undefined){
39773             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
39774             this.list.setWidth(lw);
39775             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39776         }
39777         
39778     
39779         
39780     },
39781     
39782     addItem: function(rec)
39783     {
39784         var valueField = this.combo.valueField;
39785         var displayField = this.combo.displayField;
39786         if (this.items.indexOfKey(rec[valueField]) > -1) {
39787             //console.log("GOT " + rec.data.id);
39788             return;
39789         }
39790         
39791         var x = new Roo.form.ComboBoxArray.Item({
39792             //id : rec[this.idField],
39793             data : rec,
39794             displayField : displayField ,
39795             tipField : displayField ,
39796             cb : this
39797         });
39798         // use the 
39799         this.items.add(rec[valueField],x);
39800         // add it before the element..
39801         this.updateHiddenEl();
39802         x.render(this.outerWrap, this.wrap.dom);
39803         // add the image handler..
39804     },
39805     
39806     updateHiddenEl : function()
39807     {
39808         this.validate();
39809         if (!this.hiddenEl) {
39810             return;
39811         }
39812         var ar = [];
39813         var idField = this.combo.valueField;
39814         
39815         this.items.each(function(f) {
39816             ar.push(f.data[idField]);
39817            
39818         });
39819         this.hiddenEl.dom.value = ar.join(',');
39820         this.validate();
39821     },
39822     
39823     reset : function()
39824     {
39825         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
39826         this.items.each(function(f) {
39827            f.remove(); 
39828         });
39829         this.el.dom.value = '';
39830         if (this.hiddenEl) {
39831             this.hiddenEl.dom.value = '';
39832         }
39833         
39834     },
39835     getValue: function()
39836     {
39837         return this.hiddenEl ? this.hiddenEl.dom.value : '';
39838     },
39839     setValue: function(v) // not a valid action - must use addItems..
39840     {
39841          
39842         this.reset();
39843         
39844         
39845         
39846         if (this.store.isLocal && (typeof(v) == 'string')) {
39847             // then we can use the store to find the values..
39848             // comma seperated at present.. this needs to allow JSON based encoding..
39849             this.hiddenEl.value  = v;
39850             var v_ar = [];
39851             Roo.each(v.split(','), function(k) {
39852                 Roo.log("CHECK " + this.valueField + ',' + k);
39853                 var li = this.store.query(this.valueField, k);
39854                 if (!li.length) {
39855                     return;
39856                 }
39857                 var add = {};
39858                 add[this.valueField] = k;
39859                 add[this.displayField] = li.item(0).data[this.displayField];
39860                 
39861                 this.addItem(add);
39862             }, this) 
39863              
39864         }
39865         if (typeof(v) == 'object') {
39866             // then let's assume it's an array of objects..
39867             Roo.each(v, function(l) {
39868                 this.addItem(l);
39869             }, this);
39870              
39871         }
39872         
39873         
39874     },
39875     setFromData: function(v)
39876     {
39877         // this recieves an object, if setValues is called.
39878         this.reset();
39879         this.el.dom.value = v[this.displayField];
39880         this.hiddenEl.dom.value = v[this.valueField];
39881         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
39882             return;
39883         }
39884         var kv = v[this.valueField];
39885         var dv = v[this.displayField];
39886         kv = typeof(kv) != 'string' ? '' : kv;
39887         dv = typeof(dv) != 'string' ? '' : dv;
39888         
39889         
39890         var keys = kv.split(',');
39891         var display = dv.split(',');
39892         for (var i = 0 ; i < keys.length; i++) {
39893             
39894             add = {};
39895             add[this.valueField] = keys[i];
39896             add[this.displayField] = display[i];
39897             this.addItem(add);
39898         }
39899       
39900         
39901     },
39902     
39903     
39904     validateValue : function(value){
39905         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
39906         
39907     }
39908     
39909 });
39910
39911
39912
39913 /**
39914  * @class Roo.form.ComboBoxArray.Item
39915  * @extends Roo.BoxComponent
39916  * A selected item in the list
39917  *  Fred [x]  Brian [x]  [Pick another |v]
39918  * 
39919  * @constructor
39920  * Create a new item.
39921  * @param {Object} config Configuration options
39922  */
39923  
39924 Roo.form.ComboBoxArray.Item = function(config) {
39925     config.id = Roo.id();
39926     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
39927 }
39928
39929 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
39930     data : {},
39931     cb: false,
39932     displayField : false,
39933     tipField : false,
39934     
39935     
39936     defaultAutoCreate : {
39937         tag: 'div',
39938         cls: 'x-cbarray-item',
39939         cn : [ 
39940             { tag: 'div' },
39941             {
39942                 tag: 'img',
39943                 width:16,
39944                 height : 16,
39945                 src : Roo.BLANK_IMAGE_URL ,
39946                 align: 'center'
39947             }
39948         ]
39949         
39950     },
39951     
39952  
39953     onRender : function(ct, position)
39954     {
39955         Roo.form.Field.superclass.onRender.call(this, ct, position);
39956         
39957         if(!this.el){
39958             var cfg = this.getAutoCreate();
39959             this.el = ct.createChild(cfg, position);
39960         }
39961         
39962         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
39963         
39964         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
39965             this.cb.renderer(this.data) :
39966             String.format('{0}',this.data[this.displayField]);
39967         
39968             
39969         this.el.child('div').dom.setAttribute('qtip',
39970                         String.format('{0}',this.data[this.tipField])
39971         );
39972         
39973         this.el.child('img').on('click', this.remove, this);
39974         
39975     },
39976    
39977     remove : function()
39978     {
39979         
39980         this.cb.items.remove(this);
39981         this.el.child('img').un('click', this.remove, this);
39982         this.el.remove();
39983         this.cb.updateHiddenEl();
39984     }
39985     
39986     
39987 });/*
39988  * Based on:
39989  * Ext JS Library 1.1.1
39990  * Copyright(c) 2006-2007, Ext JS, LLC.
39991  *
39992  * Originally Released Under LGPL - original licence link has changed is not relivant.
39993  *
39994  * Fork - LGPL
39995  * <script type="text/javascript">
39996  */
39997 /**
39998  * @class Roo.form.Checkbox
39999  * @extends Roo.form.Field
40000  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40001  * @constructor
40002  * Creates a new Checkbox
40003  * @param {Object} config Configuration options
40004  */
40005 Roo.form.Checkbox = function(config){
40006     Roo.form.Checkbox.superclass.constructor.call(this, config);
40007     this.addEvents({
40008         /**
40009          * @event check
40010          * Fires when the checkbox is checked or unchecked.
40011              * @param {Roo.form.Checkbox} this This checkbox
40012              * @param {Boolean} checked The new checked value
40013              */
40014         check : true
40015     });
40016 };
40017
40018 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40019     /**
40020      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40021      */
40022     focusClass : undefined,
40023     /**
40024      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40025      */
40026     fieldClass: "x-form-field",
40027     /**
40028      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40029      */
40030     checked: false,
40031     /**
40032      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40033      * {tag: "input", type: "checkbox", autocomplete: "off"})
40034      */
40035     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40036     /**
40037      * @cfg {String} boxLabel The text that appears beside the checkbox
40038      */
40039     boxLabel : "",
40040     /**
40041      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40042      */  
40043     inputValue : '1',
40044     /**
40045      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40046      */
40047      valueOff: '0', // value when not checked..
40048
40049     actionMode : 'viewEl', 
40050     //
40051     // private
40052     itemCls : 'x-menu-check-item x-form-item',
40053     groupClass : 'x-menu-group-item',
40054     inputType : 'hidden',
40055     
40056     
40057     inSetChecked: false, // check that we are not calling self...
40058     
40059     inputElement: false, // real input element?
40060     basedOn: false, // ????
40061     
40062     isFormField: true, // not sure where this is needed!!!!
40063
40064     onResize : function(){
40065         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40066         if(!this.boxLabel){
40067             this.el.alignTo(this.wrap, 'c-c');
40068         }
40069     },
40070
40071     initEvents : function(){
40072         Roo.form.Checkbox.superclass.initEvents.call(this);
40073         this.el.on("click", this.onClick,  this);
40074         this.el.on("change", this.onClick,  this);
40075     },
40076
40077
40078     getResizeEl : function(){
40079         return this.wrap;
40080     },
40081
40082     getPositionEl : function(){
40083         return this.wrap;
40084     },
40085
40086     // private
40087     onRender : function(ct, position){
40088         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40089         /*
40090         if(this.inputValue !== undefined){
40091             this.el.dom.value = this.inputValue;
40092         }
40093         */
40094         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40095         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40096         var viewEl = this.wrap.createChild({ 
40097             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40098         this.viewEl = viewEl;   
40099         this.wrap.on('click', this.onClick,  this); 
40100         
40101         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40102         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40103         
40104         
40105         
40106         if(this.boxLabel){
40107             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40108         //    viewEl.on('click', this.onClick,  this); 
40109         }
40110         //if(this.checked){
40111             this.setChecked(this.checked);
40112         //}else{
40113             //this.checked = this.el.dom;
40114         //}
40115
40116     },
40117
40118     // private
40119     initValue : Roo.emptyFn,
40120
40121     /**
40122      * Returns the checked state of the checkbox.
40123      * @return {Boolean} True if checked, else false
40124      */
40125     getValue : function(){
40126         if(this.el){
40127             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40128         }
40129         return this.valueOff;
40130         
40131     },
40132
40133         // private
40134     onClick : function(){ 
40135         this.setChecked(!this.checked);
40136
40137         //if(this.el.dom.checked != this.checked){
40138         //    this.setValue(this.el.dom.checked);
40139        // }
40140     },
40141
40142     /**
40143      * Sets the checked state of the checkbox.
40144      * On is always based on a string comparison between inputValue and the param.
40145      * @param {Boolean/String} value - the value to set 
40146      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40147      */
40148     setValue : function(v,suppressEvent){
40149         
40150         
40151         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40152         //if(this.el && this.el.dom){
40153         //    this.el.dom.checked = this.checked;
40154         //    this.el.dom.defaultChecked = this.checked;
40155         //}
40156         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40157         //this.fireEvent("check", this, this.checked);
40158     },
40159     // private..
40160     setChecked : function(state,suppressEvent)
40161     {
40162         if (this.inSetChecked) {
40163             this.checked = state;
40164             return;
40165         }
40166         
40167     
40168         if(this.wrap){
40169             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40170         }
40171         this.checked = state;
40172         if(suppressEvent !== true){
40173             this.fireEvent('check', this, state);
40174         }
40175         this.inSetChecked = true;
40176         this.el.dom.value = state ? this.inputValue : this.valueOff;
40177         this.inSetChecked = false;
40178         
40179     },
40180     // handle setting of hidden value by some other method!!?!?
40181     setFromHidden: function()
40182     {
40183         if(!this.el){
40184             return;
40185         }
40186         //console.log("SET FROM HIDDEN");
40187         //alert('setFrom hidden');
40188         this.setValue(this.el.dom.value);
40189     },
40190     
40191     onDestroy : function()
40192     {
40193         if(this.viewEl){
40194             Roo.get(this.viewEl).remove();
40195         }
40196          
40197         Roo.form.Checkbox.superclass.onDestroy.call(this);
40198     }
40199
40200 });/*
40201  * Based on:
40202  * Ext JS Library 1.1.1
40203  * Copyright(c) 2006-2007, Ext JS, LLC.
40204  *
40205  * Originally Released Under LGPL - original licence link has changed is not relivant.
40206  *
40207  * Fork - LGPL
40208  * <script type="text/javascript">
40209  */
40210  
40211 /**
40212  * @class Roo.form.Radio
40213  * @extends Roo.form.Checkbox
40214  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40215  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40216  * @constructor
40217  * Creates a new Radio
40218  * @param {Object} config Configuration options
40219  */
40220 Roo.form.Radio = function(){
40221     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40222 };
40223 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40224     inputType: 'radio',
40225
40226     /**
40227      * If this radio is part of a group, it will return the selected value
40228      * @return {String}
40229      */
40230     getGroupValue : function(){
40231         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40232     },
40233     
40234     
40235     onRender : function(ct, position){
40236         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40237         
40238         if(this.inputValue !== undefined){
40239             this.el.dom.value = this.inputValue;
40240         }
40241          
40242         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40243         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40244         //var viewEl = this.wrap.createChild({ 
40245         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40246         //this.viewEl = viewEl;   
40247         //this.wrap.on('click', this.onClick,  this); 
40248         
40249         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40250         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40251         
40252         
40253         
40254         if(this.boxLabel){
40255             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40256         //    viewEl.on('click', this.onClick,  this); 
40257         }
40258          if(this.checked){
40259             this.el.dom.checked =   'checked' ;
40260         }
40261          
40262     } 
40263     
40264     
40265 });//<script type="text/javascript">
40266
40267 /*
40268  * Ext JS Library 1.1.1
40269  * Copyright(c) 2006-2007, Ext JS, LLC.
40270  * licensing@extjs.com
40271  * 
40272  * http://www.extjs.com/license
40273  */
40274  
40275  /*
40276   * 
40277   * Known bugs:
40278   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
40279   * - IE ? - no idea how much works there.
40280   * 
40281   * 
40282   * 
40283   */
40284  
40285
40286 /**
40287  * @class Ext.form.HtmlEditor
40288  * @extends Ext.form.Field
40289  * Provides a lightweight HTML Editor component.
40290  *
40291  * This has been tested on Fireforx / Chrome.. IE may not be so great..
40292  * 
40293  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
40294  * supported by this editor.</b><br/><br/>
40295  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
40296  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40297  */
40298 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
40299       /**
40300      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
40301      */
40302     toolbars : false,
40303     /**
40304      * @cfg {String} createLinkText The default text for the create link prompt
40305      */
40306     createLinkText : 'Please enter the URL for the link:',
40307     /**
40308      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
40309      */
40310     defaultLinkValue : 'http:/'+'/',
40311    
40312      /**
40313      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40314      *                        Roo.resizable.
40315      */
40316     resizable : false,
40317      /**
40318      * @cfg {Number} height (in pixels)
40319      */   
40320     height: 300,
40321    /**
40322      * @cfg {Number} width (in pixels)
40323      */   
40324     width: 500,
40325     
40326     /**
40327      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40328      * 
40329      */
40330     stylesheets: false,
40331     
40332     // id of frame..
40333     frameId: false,
40334     
40335     // private properties
40336     validationEvent : false,
40337     deferHeight: true,
40338     initialized : false,
40339     activated : false,
40340     sourceEditMode : false,
40341     onFocus : Roo.emptyFn,
40342     iframePad:3,
40343     hideMode:'offsets',
40344     
40345     defaultAutoCreate : { // modified by initCompnoent..
40346         tag: "textarea",
40347         style:"width:500px;height:300px;",
40348         autocomplete: "off"
40349     },
40350
40351     // private
40352     initComponent : function(){
40353         this.addEvents({
40354             /**
40355              * @event initialize
40356              * Fires when the editor is fully initialized (including the iframe)
40357              * @param {HtmlEditor} this
40358              */
40359             initialize: true,
40360             /**
40361              * @event activate
40362              * Fires when the editor is first receives the focus. Any insertion must wait
40363              * until after this event.
40364              * @param {HtmlEditor} this
40365              */
40366             activate: true,
40367              /**
40368              * @event beforesync
40369              * Fires before the textarea is updated with content from the editor iframe. Return false
40370              * to cancel the sync.
40371              * @param {HtmlEditor} this
40372              * @param {String} html
40373              */
40374             beforesync: true,
40375              /**
40376              * @event beforepush
40377              * Fires before the iframe editor is updated with content from the textarea. Return false
40378              * to cancel the push.
40379              * @param {HtmlEditor} this
40380              * @param {String} html
40381              */
40382             beforepush: true,
40383              /**
40384              * @event sync
40385              * Fires when the textarea is updated with content from the editor iframe.
40386              * @param {HtmlEditor} this
40387              * @param {String} html
40388              */
40389             sync: true,
40390              /**
40391              * @event push
40392              * Fires when the iframe editor is updated with content from the textarea.
40393              * @param {HtmlEditor} this
40394              * @param {String} html
40395              */
40396             push: true,
40397              /**
40398              * @event editmodechange
40399              * Fires when the editor switches edit modes
40400              * @param {HtmlEditor} this
40401              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
40402              */
40403             editmodechange: true,
40404             /**
40405              * @event editorevent
40406              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40407              * @param {HtmlEditor} this
40408              */
40409             editorevent: true
40410         });
40411         this.defaultAutoCreate =  {
40412             tag: "textarea",
40413             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
40414             autocomplete: "off"
40415         };
40416     },
40417
40418     /**
40419      * Protected method that will not generally be called directly. It
40420      * is called when the editor creates its toolbar. Override this method if you need to
40421      * add custom toolbar buttons.
40422      * @param {HtmlEditor} editor
40423      */
40424     createToolbar : function(editor){
40425         if (!editor.toolbars || !editor.toolbars.length) {
40426             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
40427         }
40428         
40429         for (var i =0 ; i < editor.toolbars.length;i++) {
40430             editor.toolbars[i] = Roo.factory(
40431                     typeof(editor.toolbars[i]) == 'string' ?
40432                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
40433                 Roo.form.HtmlEditor);
40434             editor.toolbars[i].init(editor);
40435         }
40436          
40437         
40438     },
40439
40440     /**
40441      * Protected method that will not generally be called directly. It
40442      * is called when the editor initializes the iframe with HTML contents. Override this method if you
40443      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
40444      */
40445     getDocMarkup : function(){
40446         // body styles..
40447         var st = '';
40448         if (this.stylesheets === false) {
40449             
40450             Roo.get(document.head).select('style').each(function(node) {
40451                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40452             });
40453             
40454             Roo.get(document.head).select('link').each(function(node) { 
40455                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40456             });
40457             
40458         } else if (!this.stylesheets.length) {
40459                 // simple..
40460                 st = '<style type="text/css">' +
40461                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40462                    '</style>';
40463         } else {
40464             Roo.each(this.stylesheets, function(s) {
40465                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
40466             });
40467             
40468         }
40469         
40470         st +=  '<style type="text/css">' +
40471             'IMG { cursor: pointer } ' +
40472         '</style>';
40473
40474         
40475         return '<html><head>' + st  +
40476             //<style type="text/css">' +
40477             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40478             //'</style>' +
40479             ' </head><body class="roo-htmleditor-body"></body></html>';
40480     },
40481
40482     // private
40483     onRender : function(ct, position)
40484     {
40485         var _t = this;
40486         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
40487         this.el.dom.style.border = '0 none';
40488         this.el.dom.setAttribute('tabIndex', -1);
40489         this.el.addClass('x-hidden');
40490         if(Roo.isIE){ // fix IE 1px bogus margin
40491             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
40492         }
40493         this.wrap = this.el.wrap({
40494             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
40495         });
40496         
40497         if (this.resizable) {
40498             this.resizeEl = new Roo.Resizable(this.wrap, {
40499                 pinned : true,
40500                 wrap: true,
40501                 dynamic : true,
40502                 minHeight : this.height,
40503                 height: this.height,
40504                 handles : this.resizable,
40505                 width: this.width,
40506                 listeners : {
40507                     resize : function(r, w, h) {
40508                         _t.onResize(w,h); // -something
40509                     }
40510                 }
40511             });
40512             
40513         }
40514
40515         this.frameId = Roo.id();
40516         
40517         this.createToolbar(this);
40518         
40519       
40520         
40521         var iframe = this.wrap.createChild({
40522             tag: 'iframe',
40523             id: this.frameId,
40524             name: this.frameId,
40525             frameBorder : 'no',
40526             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
40527         }, this.el
40528         );
40529         
40530        // console.log(iframe);
40531         //this.wrap.dom.appendChild(iframe);
40532
40533         this.iframe = iframe.dom;
40534
40535          this.assignDocWin();
40536         
40537         this.doc.designMode = 'on';
40538        
40539         this.doc.open();
40540         this.doc.write(this.getDocMarkup());
40541         this.doc.close();
40542
40543         
40544         var task = { // must defer to wait for browser to be ready
40545             run : function(){
40546                 //console.log("run task?" + this.doc.readyState);
40547                 this.assignDocWin();
40548                 if(this.doc.body || this.doc.readyState == 'complete'){
40549                     try {
40550                         this.doc.designMode="on";
40551                     } catch (e) {
40552                         return;
40553                     }
40554                     Roo.TaskMgr.stop(task);
40555                     this.initEditor.defer(10, this);
40556                 }
40557             },
40558             interval : 10,
40559             duration:10000,
40560             scope: this
40561         };
40562         Roo.TaskMgr.start(task);
40563
40564         if(!this.width){
40565             this.setSize(this.wrap.getSize());
40566         }
40567         if (this.resizeEl) {
40568             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
40569             // should trigger onReize..
40570         }
40571     },
40572
40573     // private
40574     onResize : function(w, h)
40575     {
40576         //Roo.log('resize: ' +w + ',' + h );
40577         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
40578         if(this.el && this.iframe){
40579             if(typeof w == 'number'){
40580                 var aw = w - this.wrap.getFrameWidth('lr');
40581                 this.el.setWidth(this.adjustWidth('textarea', aw));
40582                 this.iframe.style.width = aw + 'px';
40583             }
40584             if(typeof h == 'number'){
40585                 var tbh = 0;
40586                 for (var i =0; i < this.toolbars.length;i++) {
40587                     // fixme - ask toolbars for heights?
40588                     tbh += this.toolbars[i].tb.el.getHeight();
40589                     if (this.toolbars[i].footer) {
40590                         tbh += this.toolbars[i].footer.el.getHeight();
40591                     }
40592                 }
40593                 
40594                 
40595                 
40596                 
40597                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
40598                 ah -= 5; // knock a few pixes off for look..
40599                 this.el.setHeight(this.adjustWidth('textarea', ah));
40600                 this.iframe.style.height = ah + 'px';
40601                 if(this.doc){
40602                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
40603                 }
40604             }
40605         }
40606     },
40607
40608     /**
40609      * Toggles the editor between standard and source edit mode.
40610      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
40611      */
40612     toggleSourceEdit : function(sourceEditMode){
40613         
40614         this.sourceEditMode = sourceEditMode === true;
40615         
40616         if(this.sourceEditMode){
40617 //            Roo.log('in');
40618 //            Roo.log(this.syncValue());
40619             this.syncValue();
40620             this.iframe.className = 'x-hidden';
40621             this.el.removeClass('x-hidden');
40622             this.el.dom.removeAttribute('tabIndex');
40623             this.el.focus();
40624         }else{
40625 //            Roo.log('out')
40626 //            Roo.log(this.pushValue()); 
40627             this.pushValue();
40628             this.iframe.className = '';
40629             this.el.addClass('x-hidden');
40630             this.el.dom.setAttribute('tabIndex', -1);
40631             this.deferFocus();
40632         }
40633         this.setSize(this.wrap.getSize());
40634         this.fireEvent('editmodechange', this, this.sourceEditMode);
40635     },
40636
40637     // private used internally
40638     createLink : function(){
40639         var url = prompt(this.createLinkText, this.defaultLinkValue);
40640         if(url && url != 'http:/'+'/'){
40641             this.relayCmd('createlink', url);
40642         }
40643     },
40644
40645     // private (for BoxComponent)
40646     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40647
40648     // private (for BoxComponent)
40649     getResizeEl : function(){
40650         return this.wrap;
40651     },
40652
40653     // private (for BoxComponent)
40654     getPositionEl : function(){
40655         return this.wrap;
40656     },
40657
40658     // private
40659     initEvents : function(){
40660         this.originalValue = this.getValue();
40661     },
40662
40663     /**
40664      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40665      * @method
40666      */
40667     markInvalid : Roo.emptyFn,
40668     /**
40669      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40670      * @method
40671      */
40672     clearInvalid : Roo.emptyFn,
40673
40674     setValue : function(v){
40675         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
40676         this.pushValue();
40677     },
40678
40679     /**
40680      * Protected method that will not generally be called directly. If you need/want
40681      * custom HTML cleanup, this is the method you should override.
40682      * @param {String} html The HTML to be cleaned
40683      * return {String} The cleaned HTML
40684      */
40685     cleanHtml : function(html){
40686         html = String(html);
40687         if(html.length > 5){
40688             if(Roo.isSafari){ // strip safari nonsense
40689                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
40690             }
40691         }
40692         if(html == '&nbsp;'){
40693             html = '';
40694         }
40695         return html;
40696     },
40697
40698     /**
40699      * Protected method that will not generally be called directly. Syncs the contents
40700      * of the editor iframe with the textarea.
40701      */
40702     syncValue : function(){
40703         if(this.initialized){
40704             var bd = (this.doc.body || this.doc.documentElement);
40705             //this.cleanUpPaste(); -- this is done else where and causes havoc..
40706             var html = bd.innerHTML;
40707             if(Roo.isSafari){
40708                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
40709                 var m = bs.match(/text-align:(.*?);/i);
40710                 if(m && m[1]){
40711                     html = '<div style="'+m[0]+'">' + html + '</div>';
40712                 }
40713             }
40714             html = this.cleanHtml(html);
40715             // fix up the special chars.. normaly like back quotes in word...
40716             // however we do not want to do this with chinese..
40717             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
40718                 var cc = b.charCodeAt();
40719                 if (
40720                     (cc >= 0x4E00 && cc < 0xA000 ) ||
40721                     (cc >= 0x3400 && cc < 0x4E00 ) ||
40722                     (cc >= 0xf900 && cc < 0xfb00 )
40723                 ) {
40724                         return b;
40725                 }
40726                 return "&#"+cc+";" 
40727             });
40728             if(this.fireEvent('beforesync', this, html) !== false){
40729                 this.el.dom.value = html;
40730                 this.fireEvent('sync', this, html);
40731             }
40732         }
40733     },
40734
40735     /**
40736      * Protected method that will not generally be called directly. Pushes the value of the textarea
40737      * into the iframe editor.
40738      */
40739     pushValue : function(){
40740         if(this.initialized){
40741             var v = this.el.dom.value;
40742             
40743             if(v.length < 1){
40744                 v = '&#160;';
40745             }
40746             
40747             if(this.fireEvent('beforepush', this, v) !== false){
40748                 var d = (this.doc.body || this.doc.documentElement);
40749                 d.innerHTML = v;
40750                 this.cleanUpPaste();
40751                 this.el.dom.value = d.innerHTML;
40752                 this.fireEvent('push', this, v);
40753             }
40754         }
40755     },
40756
40757     // private
40758     deferFocus : function(){
40759         this.focus.defer(10, this);
40760     },
40761
40762     // doc'ed in Field
40763     focus : function(){
40764         if(this.win && !this.sourceEditMode){
40765             this.win.focus();
40766         }else{
40767             this.el.focus();
40768         }
40769     },
40770     
40771     assignDocWin: function()
40772     {
40773         var iframe = this.iframe;
40774         
40775          if(Roo.isIE){
40776             this.doc = iframe.contentWindow.document;
40777             this.win = iframe.contentWindow;
40778         } else {
40779             if (!Roo.get(this.frameId)) {
40780                 return;
40781             }
40782             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
40783             this.win = Roo.get(this.frameId).dom.contentWindow;
40784         }
40785     },
40786     
40787     // private
40788     initEditor : function(){
40789         //console.log("INIT EDITOR");
40790         this.assignDocWin();
40791         
40792         
40793         
40794         this.doc.designMode="on";
40795         this.doc.open();
40796         this.doc.write(this.getDocMarkup());
40797         this.doc.close();
40798         
40799         var dbody = (this.doc.body || this.doc.documentElement);
40800         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
40801         // this copies styles from the containing element into thsi one..
40802         // not sure why we need all of this..
40803         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
40804         ss['background-attachment'] = 'fixed'; // w3c
40805         dbody.bgProperties = 'fixed'; // ie
40806         Roo.DomHelper.applyStyles(dbody, ss);
40807         Roo.EventManager.on(this.doc, {
40808             //'mousedown': this.onEditorEvent,
40809             'mouseup': this.onEditorEvent,
40810             'dblclick': this.onEditorEvent,
40811             'click': this.onEditorEvent,
40812             'keyup': this.onEditorEvent,
40813             buffer:100,
40814             scope: this
40815         });
40816         if(Roo.isGecko){
40817             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
40818         }
40819         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
40820             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
40821         }
40822         this.initialized = true;
40823
40824         this.fireEvent('initialize', this);
40825         this.pushValue();
40826     },
40827
40828     // private
40829     onDestroy : function(){
40830         
40831         
40832         
40833         if(this.rendered){
40834             
40835             for (var i =0; i < this.toolbars.length;i++) {
40836                 // fixme - ask toolbars for heights?
40837                 this.toolbars[i].onDestroy();
40838             }
40839             
40840             this.wrap.dom.innerHTML = '';
40841             this.wrap.remove();
40842         }
40843     },
40844
40845     // private
40846     onFirstFocus : function(){
40847         
40848         this.assignDocWin();
40849         
40850         
40851         this.activated = true;
40852         for (var i =0; i < this.toolbars.length;i++) {
40853             this.toolbars[i].onFirstFocus();
40854         }
40855        
40856         if(Roo.isGecko){ // prevent silly gecko errors
40857             this.win.focus();
40858             var s = this.win.getSelection();
40859             if(!s.focusNode || s.focusNode.nodeType != 3){
40860                 var r = s.getRangeAt(0);
40861                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
40862                 r.collapse(true);
40863                 this.deferFocus();
40864             }
40865             try{
40866                 this.execCmd('useCSS', true);
40867                 this.execCmd('styleWithCSS', false);
40868             }catch(e){}
40869         }
40870         this.fireEvent('activate', this);
40871     },
40872
40873     // private
40874     adjustFont: function(btn){
40875         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
40876         //if(Roo.isSafari){ // safari
40877         //    adjust *= 2;
40878        // }
40879         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
40880         if(Roo.isSafari){ // safari
40881             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
40882             v =  (v < 10) ? 10 : v;
40883             v =  (v > 48) ? 48 : v;
40884             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
40885             
40886         }
40887         
40888         
40889         v = Math.max(1, v+adjust);
40890         
40891         this.execCmd('FontSize', v  );
40892     },
40893
40894     onEditorEvent : function(e){
40895         this.fireEvent('editorevent', this, e);
40896       //  this.updateToolbar();
40897         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
40898     },
40899
40900     insertTag : function(tg)
40901     {
40902         // could be a bit smarter... -> wrap the current selected tRoo..
40903         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
40904             
40905             range = this.createRange(this.getSelection());
40906             var wrappingNode = this.doc.createElement(tg.toLowerCase());
40907             wrappingNode.appendChild(range.extractContents());
40908             range.insertNode(wrappingNode);
40909
40910             return;
40911             
40912             
40913             
40914         }
40915         this.execCmd("formatblock",   tg);
40916         
40917     },
40918     
40919     insertText : function(txt)
40920     {
40921         
40922         
40923         var range = this.createRange();
40924         range.deleteContents();
40925                //alert(Sender.getAttribute('label'));
40926                
40927         range.insertNode(this.doc.createTextNode(txt));
40928     } ,
40929     
40930     // private
40931     relayBtnCmd : function(btn){
40932         this.relayCmd(btn.cmd);
40933     },
40934
40935     /**
40936      * Executes a Midas editor command on the editor document and performs necessary focus and
40937      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
40938      * @param {String} cmd The Midas command
40939      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40940      */
40941     relayCmd : function(cmd, value){
40942         this.win.focus();
40943         this.execCmd(cmd, value);
40944         this.fireEvent('editorevent', this);
40945         //this.updateToolbar();
40946         this.deferFocus();
40947     },
40948
40949     /**
40950      * Executes a Midas editor command directly on the editor document.
40951      * For visual commands, you should use {@link #relayCmd} instead.
40952      * <b>This should only be called after the editor is initialized.</b>
40953      * @param {String} cmd The Midas command
40954      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40955      */
40956     execCmd : function(cmd, value){
40957         this.doc.execCommand(cmd, false, value === undefined ? null : value);
40958         this.syncValue();
40959     },
40960  
40961  
40962    
40963     /**
40964      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
40965      * to insert tRoo.
40966      * @param {String} text | dom node.. 
40967      */
40968     insertAtCursor : function(text)
40969     {
40970         
40971         
40972         
40973         if(!this.activated){
40974             return;
40975         }
40976         /*
40977         if(Roo.isIE){
40978             this.win.focus();
40979             var r = this.doc.selection.createRange();
40980             if(r){
40981                 r.collapse(true);
40982                 r.pasteHTML(text);
40983                 this.syncValue();
40984                 this.deferFocus();
40985             
40986             }
40987             return;
40988         }
40989         */
40990         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
40991             this.win.focus();
40992             
40993             
40994             // from jquery ui (MIT licenced)
40995             var range, node;
40996             var win = this.win;
40997             
40998             if (win.getSelection && win.getSelection().getRangeAt) {
40999                 range = win.getSelection().getRangeAt(0);
41000                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41001                 range.insertNode(node);
41002             } else if (win.document.selection && win.document.selection.createRange) {
41003                 // no firefox support
41004                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41005                 win.document.selection.createRange().pasteHTML(txt);
41006             } else {
41007                 // no firefox support
41008                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41009                 this.execCmd('InsertHTML', txt);
41010             } 
41011             
41012             this.syncValue();
41013             
41014             this.deferFocus();
41015         }
41016     },
41017  // private
41018     mozKeyPress : function(e){
41019         if(e.ctrlKey){
41020             var c = e.getCharCode(), cmd;
41021           
41022             if(c > 0){
41023                 c = String.fromCharCode(c).toLowerCase();
41024                 switch(c){
41025                     case 'b':
41026                         cmd = 'bold';
41027                         break;
41028                     case 'i':
41029                         cmd = 'italic';
41030                         break;
41031                     
41032                     case 'u':
41033                         cmd = 'underline';
41034                         break;
41035                     
41036                     case 'v':
41037                         this.cleanUpPaste.defer(100, this);
41038                         return;
41039                         
41040                 }
41041                 if(cmd){
41042                     this.win.focus();
41043                     this.execCmd(cmd);
41044                     this.deferFocus();
41045                     e.preventDefault();
41046                 }
41047                 
41048             }
41049         }
41050     },
41051
41052     // private
41053     fixKeys : function(){ // load time branching for fastest keydown performance
41054         if(Roo.isIE){
41055             return function(e){
41056                 var k = e.getKey(), r;
41057                 if(k == e.TAB){
41058                     e.stopEvent();
41059                     r = this.doc.selection.createRange();
41060                     if(r){
41061                         r.collapse(true);
41062                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41063                         this.deferFocus();
41064                     }
41065                     return;
41066                 }
41067                 
41068                 if(k == e.ENTER){
41069                     r = this.doc.selection.createRange();
41070                     if(r){
41071                         var target = r.parentElement();
41072                         if(!target || target.tagName.toLowerCase() != 'li'){
41073                             e.stopEvent();
41074                             r.pasteHTML('<br />');
41075                             r.collapse(false);
41076                             r.select();
41077                         }
41078                     }
41079                 }
41080                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41081                     this.cleanUpPaste.defer(100, this);
41082                     return;
41083                 }
41084                 
41085                 
41086             };
41087         }else if(Roo.isOpera){
41088             return function(e){
41089                 var k = e.getKey();
41090                 if(k == e.TAB){
41091                     e.stopEvent();
41092                     this.win.focus();
41093                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41094                     this.deferFocus();
41095                 }
41096                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41097                     this.cleanUpPaste.defer(100, this);
41098                     return;
41099                 }
41100                 
41101             };
41102         }else if(Roo.isSafari){
41103             return function(e){
41104                 var k = e.getKey();
41105                 
41106                 if(k == e.TAB){
41107                     e.stopEvent();
41108                     this.execCmd('InsertText','\t');
41109                     this.deferFocus();
41110                     return;
41111                 }
41112                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41113                     this.cleanUpPaste.defer(100, this);
41114                     return;
41115                 }
41116                 
41117              };
41118         }
41119     }(),
41120     
41121     getAllAncestors: function()
41122     {
41123         var p = this.getSelectedNode();
41124         var a = [];
41125         if (!p) {
41126             a.push(p); // push blank onto stack..
41127             p = this.getParentElement();
41128         }
41129         
41130         
41131         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41132             a.push(p);
41133             p = p.parentNode;
41134         }
41135         a.push(this.doc.body);
41136         return a;
41137     },
41138     lastSel : false,
41139     lastSelNode : false,
41140     
41141     
41142     getSelection : function() 
41143     {
41144         this.assignDocWin();
41145         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41146     },
41147     
41148     getSelectedNode: function() 
41149     {
41150         // this may only work on Gecko!!!
41151         
41152         // should we cache this!!!!
41153         
41154         
41155         
41156          
41157         var range = this.createRange(this.getSelection()).cloneRange();
41158         
41159         if (Roo.isIE) {
41160             var parent = range.parentElement();
41161             while (true) {
41162                 var testRange = range.duplicate();
41163                 testRange.moveToElementText(parent);
41164                 if (testRange.inRange(range)) {
41165                     break;
41166                 }
41167                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41168                     break;
41169                 }
41170                 parent = parent.parentElement;
41171             }
41172             return parent;
41173         }
41174         
41175         // is ancestor a text element.
41176         var ac =  range.commonAncestorContainer;
41177         if (ac.nodeType == 3) {
41178             ac = ac.parentNode;
41179         }
41180         
41181         var ar = ac.childNodes;
41182          
41183         var nodes = [];
41184         var other_nodes = [];
41185         var has_other_nodes = false;
41186         for (var i=0;i<ar.length;i++) {
41187             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41188                 continue;
41189             }
41190             // fullly contained node.
41191             
41192             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41193                 nodes.push(ar[i]);
41194                 continue;
41195             }
41196             
41197             // probably selected..
41198             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41199                 other_nodes.push(ar[i]);
41200                 continue;
41201             }
41202             // outer..
41203             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41204                 continue;
41205             }
41206             
41207             
41208             has_other_nodes = true;
41209         }
41210         if (!nodes.length && other_nodes.length) {
41211             nodes= other_nodes;
41212         }
41213         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41214             return false;
41215         }
41216         
41217         return nodes[0];
41218     },
41219     createRange: function(sel)
41220     {
41221         // this has strange effects when using with 
41222         // top toolbar - not sure if it's a great idea.
41223         //this.editor.contentWindow.focus();
41224         if (typeof sel != "undefined") {
41225             try {
41226                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41227             } catch(e) {
41228                 return this.doc.createRange();
41229             }
41230         } else {
41231             return this.doc.createRange();
41232         }
41233     },
41234     getParentElement: function()
41235     {
41236         
41237         this.assignDocWin();
41238         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41239         
41240         var range = this.createRange(sel);
41241          
41242         try {
41243             var p = range.commonAncestorContainer;
41244             while (p.nodeType == 3) { // text node
41245                 p = p.parentNode;
41246             }
41247             return p;
41248         } catch (e) {
41249             return null;
41250         }
41251     
41252     },
41253     /***
41254      *
41255      * Range intersection.. the hard stuff...
41256      *  '-1' = before
41257      *  '0' = hits..
41258      *  '1' = after.
41259      *         [ -- selected range --- ]
41260      *   [fail]                        [fail]
41261      *
41262      *    basically..
41263      *      if end is before start or  hits it. fail.
41264      *      if start is after end or hits it fail.
41265      *
41266      *   if either hits (but other is outside. - then it's not 
41267      *   
41268      *    
41269      **/
41270     
41271     
41272     // @see http://www.thismuchiknow.co.uk/?p=64.
41273     rangeIntersectsNode : function(range, node)
41274     {
41275         var nodeRange = node.ownerDocument.createRange();
41276         try {
41277             nodeRange.selectNode(node);
41278         } catch (e) {
41279             nodeRange.selectNodeContents(node);
41280         }
41281     
41282         var rangeStartRange = range.cloneRange();
41283         rangeStartRange.collapse(true);
41284     
41285         var rangeEndRange = range.cloneRange();
41286         rangeEndRange.collapse(false);
41287     
41288         var nodeStartRange = nodeRange.cloneRange();
41289         nodeStartRange.collapse(true);
41290     
41291         var nodeEndRange = nodeRange.cloneRange();
41292         nodeEndRange.collapse(false);
41293     
41294         return rangeStartRange.compareBoundaryPoints(
41295                  Range.START_TO_START, nodeEndRange) == -1 &&
41296                rangeEndRange.compareBoundaryPoints(
41297                  Range.START_TO_START, nodeStartRange) == 1;
41298         
41299          
41300     },
41301     rangeCompareNode : function(range, node)
41302     {
41303         var nodeRange = node.ownerDocument.createRange();
41304         try {
41305             nodeRange.selectNode(node);
41306         } catch (e) {
41307             nodeRange.selectNodeContents(node);
41308         }
41309         
41310         
41311         range.collapse(true);
41312     
41313         nodeRange.collapse(true);
41314      
41315         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41316         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41317          
41318         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41319         
41320         var nodeIsBefore   =  ss == 1;
41321         var nodeIsAfter    = ee == -1;
41322         
41323         if (nodeIsBefore && nodeIsAfter)
41324             return 0; // outer
41325         if (!nodeIsBefore && nodeIsAfter)
41326             return 1; //right trailed.
41327         
41328         if (nodeIsBefore && !nodeIsAfter)
41329             return 2;  // left trailed.
41330         // fully contined.
41331         return 3;
41332     },
41333
41334     // private? - in a new class?
41335     cleanUpPaste :  function()
41336     {
41337         // cleans up the whole document..
41338          Roo.log('cleanuppaste');
41339         this.cleanUpChildren(this.doc.body);
41340         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41341         if (clean != this.doc.body.innerHTML) {
41342             this.doc.body.innerHTML = clean;
41343         }
41344         
41345     },
41346     
41347     cleanWordChars : function(input) {// change the chars to hex code
41348         var he = Roo.form.HtmlEditor;
41349         
41350         var output = input;
41351         Roo.each(he.swapCodes, function(sw) { 
41352             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41353             
41354             output = output.replace(swapper, sw[1]);
41355         });
41356         
41357         return output;
41358     },
41359     
41360     
41361     cleanUpChildren : function (n)
41362     {
41363         if (!n.childNodes.length) {
41364             return;
41365         }
41366         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41367            this.cleanUpChild(n.childNodes[i]);
41368         }
41369     },
41370     
41371     
41372         
41373     
41374     cleanUpChild : function (node)
41375     {
41376         var ed = this;
41377         //console.log(node);
41378         if (node.nodeName == "#text") {
41379             // clean up silly Windows -- stuff?
41380             return; 
41381         }
41382         if (node.nodeName == "#comment") {
41383             node.parentNode.removeChild(node);
41384             // clean up silly Windows -- stuff?
41385             return; 
41386         }
41387         
41388         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
41389             // remove node.
41390             node.parentNode.removeChild(node);
41391             return;
41392             
41393         }
41394         
41395         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
41396         
41397         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41398         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41399         
41400         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41401         //    remove_keep_children = true;
41402         //}
41403         
41404         if (remove_keep_children) {
41405             this.cleanUpChildren(node);
41406             // inserts everything just before this node...
41407             while (node.childNodes.length) {
41408                 var cn = node.childNodes[0];
41409                 node.removeChild(cn);
41410                 node.parentNode.insertBefore(cn, node);
41411             }
41412             node.parentNode.removeChild(node);
41413             return;
41414         }
41415         
41416         if (!node.attributes || !node.attributes.length) {
41417             this.cleanUpChildren(node);
41418             return;
41419         }
41420         
41421         function cleanAttr(n,v)
41422         {
41423             
41424             if (v.match(/^\./) || v.match(/^\//)) {
41425                 return;
41426             }
41427             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41428                 return;
41429             }
41430             if (v.match(/^#/)) {
41431                 return;
41432             }
41433 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41434             node.removeAttribute(n);
41435             
41436         }
41437         
41438         function cleanStyle(n,v)
41439         {
41440             if (v.match(/expression/)) { //XSS?? should we even bother..
41441                 node.removeAttribute(n);
41442                 return;
41443             }
41444             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
41445             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
41446             
41447             
41448             var parts = v.split(/;/);
41449             var clean = [];
41450             
41451             Roo.each(parts, function(p) {
41452                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
41453                 if (!p.length) {
41454                     return true;
41455                 }
41456                 var l = p.split(':').shift().replace(/\s+/g,'');
41457                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
41458                 
41459                 
41460                 if ( cblack.indexOf(l) > -1) {
41461 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41462                     //node.removeAttribute(n);
41463                     return true;
41464                 }
41465                 //Roo.log()
41466                 // only allow 'c whitelisted system attributes'
41467                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
41468 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41469                     //node.removeAttribute(n);
41470                     return true;
41471                 }
41472                 
41473                 
41474                  
41475                 
41476                 clean.push(p);
41477                 return true;
41478             });
41479             if (clean.length) { 
41480                 node.setAttribute(n, clean.join(';'));
41481             } else {
41482                 node.removeAttribute(n);
41483             }
41484             
41485         }
41486         
41487         
41488         for (var i = node.attributes.length-1; i > -1 ; i--) {
41489             var a = node.attributes[i];
41490             //console.log(a);
41491             
41492             if (a.name.toLowerCase().substr(0,2)=='on')  {
41493                 node.removeAttribute(a.name);
41494                 continue;
41495             }
41496             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
41497                 node.removeAttribute(a.name);
41498                 continue;
41499             }
41500             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
41501                 cleanAttr(a.name,a.value); // fixme..
41502                 continue;
41503             }
41504             if (a.name == 'style') {
41505                 cleanStyle(a.name,a.value);
41506                 continue;
41507             }
41508             /// clean up MS crap..
41509             // tecnically this should be a list of valid class'es..
41510             
41511             
41512             if (a.name == 'class') {
41513                 if (a.value.match(/^Mso/)) {
41514                     node.className = '';
41515                 }
41516                 
41517                 if (a.value.match(/body/)) {
41518                     node.className = '';
41519                 }
41520                 continue;
41521             }
41522             
41523             // style cleanup!?
41524             // class cleanup?
41525             
41526         }
41527         
41528         
41529         this.cleanUpChildren(node);
41530         
41531         
41532     }
41533     
41534     
41535     // hide stuff that is not compatible
41536     /**
41537      * @event blur
41538      * @hide
41539      */
41540     /**
41541      * @event change
41542      * @hide
41543      */
41544     /**
41545      * @event focus
41546      * @hide
41547      */
41548     /**
41549      * @event specialkey
41550      * @hide
41551      */
41552     /**
41553      * @cfg {String} fieldClass @hide
41554      */
41555     /**
41556      * @cfg {String} focusClass @hide
41557      */
41558     /**
41559      * @cfg {String} autoCreate @hide
41560      */
41561     /**
41562      * @cfg {String} inputType @hide
41563      */
41564     /**
41565      * @cfg {String} invalidClass @hide
41566      */
41567     /**
41568      * @cfg {String} invalidText @hide
41569      */
41570     /**
41571      * @cfg {String} msgFx @hide
41572      */
41573     /**
41574      * @cfg {String} validateOnBlur @hide
41575      */
41576 });
41577
41578 Roo.form.HtmlEditor.white = [
41579         'area', 'br', 'img', 'input', 'hr', 'wbr',
41580         
41581        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
41582        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
41583        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
41584        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
41585        'table',   'ul',         'xmp', 
41586        
41587        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
41588       'thead',   'tr', 
41589      
41590       'dir', 'menu', 'ol', 'ul', 'dl',
41591        
41592       'embed',  'object'
41593 ];
41594
41595
41596 Roo.form.HtmlEditor.black = [
41597     //    'embed',  'object', // enable - backend responsiblity to clean thiese
41598         'applet', // 
41599         'base',   'basefont', 'bgsound', 'blink',  'body', 
41600         'frame',  'frameset', 'head',    'html',   'ilayer', 
41601         'iframe', 'layer',  'link',     'meta',    'object',   
41602         'script', 'style' ,'title',  'xml' // clean later..
41603 ];
41604 Roo.form.HtmlEditor.clean = [
41605     'script', 'style', 'title', 'xml'
41606 ];
41607 Roo.form.HtmlEditor.remove = [
41608     'font'
41609 ];
41610 // attributes..
41611
41612 Roo.form.HtmlEditor.ablack = [
41613     'on'
41614 ];
41615     
41616 Roo.form.HtmlEditor.aclean = [ 
41617     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
41618 ];
41619
41620 // protocols..
41621 Roo.form.HtmlEditor.pwhite= [
41622         'http',  'https',  'mailto'
41623 ];
41624
41625 // white listed style attributes.
41626 Roo.form.HtmlEditor.cwhite= [
41627       //  'text-align', /// default is to allow most things..
41628       
41629          
41630 //        'font-size'//??
41631 ];
41632
41633 // black listed style attributes.
41634 Roo.form.HtmlEditor.cblack= [
41635       //  'font-size' -- this can be set by the project 
41636 ];
41637
41638
41639 Roo.form.HtmlEditor.swapCodes   =[ 
41640     [    8211, "--" ], 
41641     [    8212, "--" ], 
41642     [    8216,  "'" ],  
41643     [    8217, "'" ],  
41644     [    8220, '"' ],  
41645     [    8221, '"' ],  
41646     [    8226, "*" ],  
41647     [    8230, "..." ]
41648 ]; 
41649
41650     // <script type="text/javascript">
41651 /*
41652  * Based on
41653  * Ext JS Library 1.1.1
41654  * Copyright(c) 2006-2007, Ext JS, LLC.
41655  *  
41656  
41657  */
41658
41659 /**
41660  * @class Roo.form.HtmlEditorToolbar1
41661  * Basic Toolbar
41662  * 
41663  * Usage:
41664  *
41665  new Roo.form.HtmlEditor({
41666     ....
41667     toolbars : [
41668         new Roo.form.HtmlEditorToolbar1({
41669             disable : { fonts: 1 , format: 1, ..., ... , ...],
41670             btns : [ .... ]
41671         })
41672     }
41673      
41674  * 
41675  * @cfg {Object} disable List of elements to disable..
41676  * @cfg {Array} btns List of additional buttons.
41677  * 
41678  * 
41679  * NEEDS Extra CSS? 
41680  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
41681  */
41682  
41683 Roo.form.HtmlEditor.ToolbarStandard = function(config)
41684 {
41685     
41686     Roo.apply(this, config);
41687     
41688     // default disabled, based on 'good practice'..
41689     this.disable = this.disable || {};
41690     Roo.applyIf(this.disable, {
41691         fontSize : true,
41692         colors : true,
41693         specialElements : true
41694     });
41695     
41696     
41697     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
41698     // dont call parent... till later.
41699 }
41700
41701 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
41702     
41703     tb: false,
41704     
41705     rendered: false,
41706     
41707     editor : false,
41708     /**
41709      * @cfg {Object} disable  List of toolbar elements to disable
41710          
41711      */
41712     disable : false,
41713       /**
41714      * @cfg {Array} fontFamilies An array of available font families
41715      */
41716     fontFamilies : [
41717         'Arial',
41718         'Courier New',
41719         'Tahoma',
41720         'Times New Roman',
41721         'Verdana'
41722     ],
41723     
41724     specialChars : [
41725            "&#169;",
41726           "&#174;",     
41727           "&#8482;",    
41728           "&#163;" ,    
41729          // "&#8212;",    
41730           "&#8230;",    
41731           "&#247;" ,    
41732         //  "&#225;" ,     ?? a acute?
41733            "&#8364;"    , //Euro
41734        //   "&#8220;"    ,
41735         //  "&#8221;"    ,
41736         //  "&#8226;"    ,
41737           "&#176;"  //   , // degrees
41738
41739          // "&#233;"     , // e ecute
41740          // "&#250;"     , // u ecute?
41741     ],
41742     
41743     specialElements : [
41744         {
41745             text: "Insert Table",
41746             xtype: 'MenuItem',
41747             xns : Roo.Menu,
41748             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
41749                 
41750         },
41751         {    
41752             text: "Insert Image",
41753             xtype: 'MenuItem',
41754             xns : Roo.Menu,
41755             ihtml : '<img src="about:blank"/>'
41756             
41757         }
41758         
41759          
41760     ],
41761     
41762     
41763     inputElements : [ 
41764             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
41765             "input:submit", "input:button", "select", "textarea", "label" ],
41766     formats : [
41767         ["p"] ,  
41768         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
41769         ["pre"],[ "code"], 
41770         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
41771         ['div'],['span']
41772     ],
41773      /**
41774      * @cfg {String} defaultFont default font to use.
41775      */
41776     defaultFont: 'tahoma',
41777    
41778     fontSelect : false,
41779     
41780     
41781     formatCombo : false,
41782     
41783     init : function(editor)
41784     {
41785         this.editor = editor;
41786         
41787         
41788         var fid = editor.frameId;
41789         var etb = this;
41790         function btn(id, toggle, handler){
41791             var xid = fid + '-'+ id ;
41792             return {
41793                 id : xid,
41794                 cmd : id,
41795                 cls : 'x-btn-icon x-edit-'+id,
41796                 enableToggle:toggle !== false,
41797                 scope: editor, // was editor...
41798                 handler:handler||editor.relayBtnCmd,
41799                 clickEvent:'mousedown',
41800                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
41801                 tabIndex:-1
41802             };
41803         }
41804         
41805         
41806         
41807         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
41808         this.tb = tb;
41809          // stop form submits
41810         tb.el.on('click', function(e){
41811             e.preventDefault(); // what does this do?
41812         });
41813
41814         if(!this.disable.font) { // && !Roo.isSafari){
41815             /* why no safari for fonts 
41816             editor.fontSelect = tb.el.createChild({
41817                 tag:'select',
41818                 tabIndex: -1,
41819                 cls:'x-font-select',
41820                 html: this.createFontOptions()
41821             });
41822             
41823             editor.fontSelect.on('change', function(){
41824                 var font = editor.fontSelect.dom.value;
41825                 editor.relayCmd('fontname', font);
41826                 editor.deferFocus();
41827             }, editor);
41828             
41829             tb.add(
41830                 editor.fontSelect.dom,
41831                 '-'
41832             );
41833             */
41834             
41835         };
41836         if(!this.disable.formats){
41837             this.formatCombo = new Roo.form.ComboBox({
41838                 store: new Roo.data.SimpleStore({
41839                     id : 'tag',
41840                     fields: ['tag'],
41841                     data : this.formats // from states.js
41842                 }),
41843                 blockFocus : true,
41844                 name : '',
41845                 //autoCreate : {tag: "div",  size: "20"},
41846                 displayField:'tag',
41847                 typeAhead: false,
41848                 mode: 'local',
41849                 editable : false,
41850                 triggerAction: 'all',
41851                 emptyText:'Add tag',
41852                 selectOnFocus:true,
41853                 width:135,
41854                 listeners : {
41855                     'select': function(c, r, i) {
41856                         editor.insertTag(r.get('tag'));
41857                         editor.focus();
41858                     }
41859                 }
41860
41861             });
41862             tb.addField(this.formatCombo);
41863             
41864         }
41865         
41866         if(!this.disable.format){
41867             tb.add(
41868                 btn('bold'),
41869                 btn('italic'),
41870                 btn('underline')
41871             );
41872         };
41873         if(!this.disable.fontSize){
41874             tb.add(
41875                 '-',
41876                 
41877                 
41878                 btn('increasefontsize', false, editor.adjustFont),
41879                 btn('decreasefontsize', false, editor.adjustFont)
41880             );
41881         };
41882         
41883         
41884         if(!this.disable.colors){
41885             tb.add(
41886                 '-', {
41887                     id:editor.frameId +'-forecolor',
41888                     cls:'x-btn-icon x-edit-forecolor',
41889                     clickEvent:'mousedown',
41890                     tooltip: this.buttonTips['forecolor'] || undefined,
41891                     tabIndex:-1,
41892                     menu : new Roo.menu.ColorMenu({
41893                         allowReselect: true,
41894                         focus: Roo.emptyFn,
41895                         value:'000000',
41896                         plain:true,
41897                         selectHandler: function(cp, color){
41898                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
41899                             editor.deferFocus();
41900                         },
41901                         scope: editor,
41902                         clickEvent:'mousedown'
41903                     })
41904                 }, {
41905                     id:editor.frameId +'backcolor',
41906                     cls:'x-btn-icon x-edit-backcolor',
41907                     clickEvent:'mousedown',
41908                     tooltip: this.buttonTips['backcolor'] || undefined,
41909                     tabIndex:-1,
41910                     menu : new Roo.menu.ColorMenu({
41911                         focus: Roo.emptyFn,
41912                         value:'FFFFFF',
41913                         plain:true,
41914                         allowReselect: true,
41915                         selectHandler: function(cp, color){
41916                             if(Roo.isGecko){
41917                                 editor.execCmd('useCSS', false);
41918                                 editor.execCmd('hilitecolor', color);
41919                                 editor.execCmd('useCSS', true);
41920                                 editor.deferFocus();
41921                             }else{
41922                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
41923                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
41924                                 editor.deferFocus();
41925                             }
41926                         },
41927                         scope:editor,
41928                         clickEvent:'mousedown'
41929                     })
41930                 }
41931             );
41932         };
41933         // now add all the items...
41934         
41935
41936         if(!this.disable.alignments){
41937             tb.add(
41938                 '-',
41939                 btn('justifyleft'),
41940                 btn('justifycenter'),
41941                 btn('justifyright')
41942             );
41943         };
41944
41945         //if(!Roo.isSafari){
41946             if(!this.disable.links){
41947                 tb.add(
41948                     '-',
41949                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
41950                 );
41951             };
41952
41953             if(!this.disable.lists){
41954                 tb.add(
41955                     '-',
41956                     btn('insertorderedlist'),
41957                     btn('insertunorderedlist')
41958                 );
41959             }
41960             if(!this.disable.sourceEdit){
41961                 tb.add(
41962                     '-',
41963                     btn('sourceedit', true, function(btn){
41964                         this.toggleSourceEdit(btn.pressed);
41965                     })
41966                 );
41967             }
41968         //}
41969         
41970         var smenu = { };
41971         // special menu.. - needs to be tidied up..
41972         if (!this.disable.special) {
41973             smenu = {
41974                 text: "&#169;",
41975                 cls: 'x-edit-none',
41976                 
41977                 menu : {
41978                     items : []
41979                 }
41980             };
41981             for (var i =0; i < this.specialChars.length; i++) {
41982                 smenu.menu.items.push({
41983                     
41984                     html: this.specialChars[i],
41985                     handler: function(a,b) {
41986                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
41987                         //editor.insertAtCursor(a.html);
41988                         
41989                     },
41990                     tabIndex:-1
41991                 });
41992             }
41993             
41994             
41995             tb.add(smenu);
41996             
41997             
41998         }
41999          
42000         if (!this.disable.specialElements) {
42001             var semenu = {
42002                 text: "Other;",
42003                 cls: 'x-edit-none',
42004                 menu : {
42005                     items : []
42006                 }
42007             };
42008             for (var i =0; i < this.specialElements.length; i++) {
42009                 semenu.menu.items.push(
42010                     Roo.apply({ 
42011                         handler: function(a,b) {
42012                             editor.insertAtCursor(this.ihtml);
42013                         }
42014                     }, this.specialElements[i])
42015                 );
42016                     
42017             }
42018             
42019             tb.add(semenu);
42020             
42021             
42022         }
42023          
42024         
42025         if (this.btns) {
42026             for(var i =0; i< this.btns.length;i++) {
42027                 var b = Roo.factory(this.btns[i],Roo.form);
42028                 b.cls =  'x-edit-none';
42029                 b.scope = editor;
42030                 tb.add(b);
42031             }
42032         
42033         }
42034         
42035         
42036         
42037         // disable everything...
42038         
42039         this.tb.items.each(function(item){
42040            if(item.id != editor.frameId+ '-sourceedit'){
42041                 item.disable();
42042             }
42043         });
42044         this.rendered = true;
42045         
42046         // the all the btns;
42047         editor.on('editorevent', this.updateToolbar, this);
42048         // other toolbars need to implement this..
42049         //editor.on('editmodechange', this.updateToolbar, this);
42050     },
42051     
42052     
42053     
42054     /**
42055      * Protected method that will not generally be called directly. It triggers
42056      * a toolbar update by reading the markup state of the current selection in the editor.
42057      */
42058     updateToolbar: function(){
42059
42060         if(!this.editor.activated){
42061             this.editor.onFirstFocus();
42062             return;
42063         }
42064
42065         var btns = this.tb.items.map, 
42066             doc = this.editor.doc,
42067             frameId = this.editor.frameId;
42068
42069         if(!this.disable.font && !Roo.isSafari){
42070             /*
42071             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
42072             if(name != this.fontSelect.dom.value){
42073                 this.fontSelect.dom.value = name;
42074             }
42075             */
42076         }
42077         if(!this.disable.format){
42078             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
42079             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
42080             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
42081         }
42082         if(!this.disable.alignments){
42083             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
42084             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
42085             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
42086         }
42087         if(!Roo.isSafari && !this.disable.lists){
42088             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
42089             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
42090         }
42091         
42092         var ans = this.editor.getAllAncestors();
42093         if (this.formatCombo) {
42094             
42095             
42096             var store = this.formatCombo.store;
42097             this.formatCombo.setValue("");
42098             for (var i =0; i < ans.length;i++) {
42099                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
42100                     // select it..
42101                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
42102                     break;
42103                 }
42104             }
42105         }
42106         
42107         
42108         
42109         // hides menus... - so this cant be on a menu...
42110         Roo.menu.MenuMgr.hideAll();
42111
42112         //this.editorsyncValue();
42113     },
42114    
42115     
42116     createFontOptions : function(){
42117         var buf = [], fs = this.fontFamilies, ff, lc;
42118         
42119         
42120         
42121         for(var i = 0, len = fs.length; i< len; i++){
42122             ff = fs[i];
42123             lc = ff.toLowerCase();
42124             buf.push(
42125                 '<option value="',lc,'" style="font-family:',ff,';"',
42126                     (this.defaultFont == lc ? ' selected="true">' : '>'),
42127                     ff,
42128                 '</option>'
42129             );
42130         }
42131         return buf.join('');
42132     },
42133     
42134     toggleSourceEdit : function(sourceEditMode){
42135         if(sourceEditMode === undefined){
42136             sourceEditMode = !this.sourceEditMode;
42137         }
42138         this.sourceEditMode = sourceEditMode === true;
42139         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
42140         // just toggle the button?
42141         if(btn.pressed !== this.editor.sourceEditMode){
42142             btn.toggle(this.editor.sourceEditMode);
42143             return;
42144         }
42145         
42146         if(this.sourceEditMode){
42147             this.tb.items.each(function(item){
42148                 if(item.cmd != 'sourceedit'){
42149                     item.disable();
42150                 }
42151             });
42152           
42153         }else{
42154             if(this.initialized){
42155                 this.tb.items.each(function(item){
42156                     item.enable();
42157                 });
42158             }
42159             
42160         }
42161         // tell the editor that it's been pressed..
42162         this.editor.toggleSourceEdit(sourceEditMode);
42163        
42164     },
42165      /**
42166      * Object collection of toolbar tooltips for the buttons in the editor. The key
42167      * is the command id associated with that button and the value is a valid QuickTips object.
42168      * For example:
42169 <pre><code>
42170 {
42171     bold : {
42172         title: 'Bold (Ctrl+B)',
42173         text: 'Make the selected text bold.',
42174         cls: 'x-html-editor-tip'
42175     },
42176     italic : {
42177         title: 'Italic (Ctrl+I)',
42178         text: 'Make the selected text italic.',
42179         cls: 'x-html-editor-tip'
42180     },
42181     ...
42182 </code></pre>
42183     * @type Object
42184      */
42185     buttonTips : {
42186         bold : {
42187             title: 'Bold (Ctrl+B)',
42188             text: 'Make the selected text bold.',
42189             cls: 'x-html-editor-tip'
42190         },
42191         italic : {
42192             title: 'Italic (Ctrl+I)',
42193             text: 'Make the selected text italic.',
42194             cls: 'x-html-editor-tip'
42195         },
42196         underline : {
42197             title: 'Underline (Ctrl+U)',
42198             text: 'Underline the selected text.',
42199             cls: 'x-html-editor-tip'
42200         },
42201         increasefontsize : {
42202             title: 'Grow Text',
42203             text: 'Increase the font size.',
42204             cls: 'x-html-editor-tip'
42205         },
42206         decreasefontsize : {
42207             title: 'Shrink Text',
42208             text: 'Decrease the font size.',
42209             cls: 'x-html-editor-tip'
42210         },
42211         backcolor : {
42212             title: 'Text Highlight Color',
42213             text: 'Change the background color of the selected text.',
42214             cls: 'x-html-editor-tip'
42215         },
42216         forecolor : {
42217             title: 'Font Color',
42218             text: 'Change the color of the selected text.',
42219             cls: 'x-html-editor-tip'
42220         },
42221         justifyleft : {
42222             title: 'Align Text Left',
42223             text: 'Align text to the left.',
42224             cls: 'x-html-editor-tip'
42225         },
42226         justifycenter : {
42227             title: 'Center Text',
42228             text: 'Center text in the editor.',
42229             cls: 'x-html-editor-tip'
42230         },
42231         justifyright : {
42232             title: 'Align Text Right',
42233             text: 'Align text to the right.',
42234             cls: 'x-html-editor-tip'
42235         },
42236         insertunorderedlist : {
42237             title: 'Bullet List',
42238             text: 'Start a bulleted list.',
42239             cls: 'x-html-editor-tip'
42240         },
42241         insertorderedlist : {
42242             title: 'Numbered List',
42243             text: 'Start a numbered list.',
42244             cls: 'x-html-editor-tip'
42245         },
42246         createlink : {
42247             title: 'Hyperlink',
42248             text: 'Make the selected text a hyperlink.',
42249             cls: 'x-html-editor-tip'
42250         },
42251         sourceedit : {
42252             title: 'Source Edit',
42253             text: 'Switch to source editing mode.',
42254             cls: 'x-html-editor-tip'
42255         }
42256     },
42257     // private
42258     onDestroy : function(){
42259         if(this.rendered){
42260             
42261             this.tb.items.each(function(item){
42262                 if(item.menu){
42263                     item.menu.removeAll();
42264                     if(item.menu.el){
42265                         item.menu.el.destroy();
42266                     }
42267                 }
42268                 item.destroy();
42269             });
42270              
42271         }
42272     },
42273     onFirstFocus: function() {
42274         this.tb.items.each(function(item){
42275            item.enable();
42276         });
42277     }
42278 });
42279
42280
42281
42282
42283 // <script type="text/javascript">
42284 /*
42285  * Based on
42286  * Ext JS Library 1.1.1
42287  * Copyright(c) 2006-2007, Ext JS, LLC.
42288  *  
42289  
42290  */
42291
42292  
42293 /**
42294  * @class Roo.form.HtmlEditor.ToolbarContext
42295  * Context Toolbar
42296  * 
42297  * Usage:
42298  *
42299  new Roo.form.HtmlEditor({
42300     ....
42301     toolbars : [
42302         { xtype: 'ToolbarStandard', styles : {} }
42303         { xtype: 'ToolbarContext', disable : {} }
42304     ]
42305 })
42306
42307      
42308  * 
42309  * @config : {Object} disable List of elements to disable.. (not done yet.)
42310  * @config : {Object} styles  Map of styles available.
42311  * 
42312  */
42313
42314 Roo.form.HtmlEditor.ToolbarContext = function(config)
42315 {
42316     
42317     Roo.apply(this, config);
42318     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42319     // dont call parent... till later.
42320     this.styles = this.styles || {};
42321 }
42322
42323  
42324
42325 Roo.form.HtmlEditor.ToolbarContext.types = {
42326     'IMG' : {
42327         width : {
42328             title: "Width",
42329             width: 40
42330         },
42331         height:  {
42332             title: "Height",
42333             width: 40
42334         },
42335         align: {
42336             title: "Align",
42337             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
42338             width : 80
42339             
42340         },
42341         border: {
42342             title: "Border",
42343             width: 40
42344         },
42345         alt: {
42346             title: "Alt",
42347             width: 120
42348         },
42349         src : {
42350             title: "Src",
42351             width: 220
42352         }
42353         
42354     },
42355     'A' : {
42356         name : {
42357             title: "Name",
42358             width: 50
42359         },
42360         href:  {
42361             title: "Href",
42362             width: 220
42363         } // border?
42364         
42365     },
42366     'TABLE' : {
42367         rows : {
42368             title: "Rows",
42369             width: 20
42370         },
42371         cols : {
42372             title: "Cols",
42373             width: 20
42374         },
42375         width : {
42376             title: "Width",
42377             width: 40
42378         },
42379         height : {
42380             title: "Height",
42381             width: 40
42382         },
42383         border : {
42384             title: "Border",
42385             width: 20
42386         }
42387     },
42388     'TD' : {
42389         width : {
42390             title: "Width",
42391             width: 40
42392         },
42393         height : {
42394             title: "Height",
42395             width: 40
42396         },   
42397         align: {
42398             title: "Align",
42399             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
42400             width: 80
42401         },
42402         valign: {
42403             title: "Valign",
42404             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
42405             width: 80
42406         },
42407         colspan: {
42408             title: "Colspan",
42409             width: 20
42410             
42411         },
42412          'font-family'  : {
42413             title : "Font",
42414             style : 'fontFamily',
42415             displayField: 'display',
42416             optname : 'font-family',
42417             width: 140
42418         }
42419     },
42420     'INPUT' : {
42421         name : {
42422             title: "name",
42423             width: 120
42424         },
42425         value : {
42426             title: "Value",
42427             width: 120
42428         },
42429         width : {
42430             title: "Width",
42431             width: 40
42432         }
42433     },
42434     'LABEL' : {
42435         'for' : {
42436             title: "For",
42437             width: 120
42438         }
42439     },
42440     'TEXTAREA' : {
42441           name : {
42442             title: "name",
42443             width: 120
42444         },
42445         rows : {
42446             title: "Rows",
42447             width: 20
42448         },
42449         cols : {
42450             title: "Cols",
42451             width: 20
42452         }
42453     },
42454     'SELECT' : {
42455         name : {
42456             title: "name",
42457             width: 120
42458         },
42459         selectoptions : {
42460             title: "Options",
42461             width: 200
42462         }
42463     },
42464     
42465     // should we really allow this??
42466     // should this just be 
42467     'BODY' : {
42468         title : {
42469             title: "Title",
42470             width: 200,
42471             disabled : true
42472         }
42473     },
42474     'SPAN' : {
42475         'font-family'  : {
42476             title : "Font",
42477             style : 'fontFamily',
42478             displayField: 'display',
42479             optname : 'font-family',
42480             width: 140
42481         }
42482     },
42483     'DIV' : {
42484         'font-family'  : {
42485             title : "Font",
42486             style : 'fontFamily',
42487             displayField: 'display',
42488             optname : 'font-family',
42489             width: 140
42490         }
42491     },
42492      'P' : {
42493         'font-family'  : {
42494             title : "Font",
42495             style : 'fontFamily',
42496             displayField: 'display',
42497             optname : 'font-family',
42498             width: 140
42499         }
42500     },
42501     
42502     '*' : {
42503         // empty..
42504     }
42505
42506 };
42507
42508 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
42509 Roo.form.HtmlEditor.ToolbarContext.stores = false;
42510
42511 Roo.form.HtmlEditor.ToolbarContext.options = {
42512         'font-family'  : [ 
42513                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
42514                 [ 'Courier New', 'Courier New'],
42515                 [ 'Tahoma', 'Tahoma'],
42516                 [ 'Times New Roman,serif', 'Times'],
42517                 [ 'Verdana','Verdana' ]
42518         ]
42519 };
42520
42521 // fixme - these need to be configurable..
42522  
42523
42524 Roo.form.HtmlEditor.ToolbarContext.types
42525
42526
42527 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
42528     
42529     tb: false,
42530     
42531     rendered: false,
42532     
42533     editor : false,
42534     /**
42535      * @cfg {Object} disable  List of toolbar elements to disable
42536          
42537      */
42538     disable : false,
42539     /**
42540      * @cfg {Object} styles List of styles 
42541      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
42542      *
42543      * These must be defined in the page, so they get rendered correctly..
42544      * .headline { }
42545      * TD.underline { }
42546      * 
42547      */
42548     styles : false,
42549     
42550     options: false,
42551     
42552     toolbars : false,
42553     
42554     init : function(editor)
42555     {
42556         this.editor = editor;
42557         
42558         
42559         var fid = editor.frameId;
42560         var etb = this;
42561         function btn(id, toggle, handler){
42562             var xid = fid + '-'+ id ;
42563             return {
42564                 id : xid,
42565                 cmd : id,
42566                 cls : 'x-btn-icon x-edit-'+id,
42567                 enableToggle:toggle !== false,
42568                 scope: editor, // was editor...
42569                 handler:handler||editor.relayBtnCmd,
42570                 clickEvent:'mousedown',
42571                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42572                 tabIndex:-1
42573             };
42574         }
42575         // create a new element.
42576         var wdiv = editor.wrap.createChild({
42577                 tag: 'div'
42578             }, editor.wrap.dom.firstChild.nextSibling, true);
42579         
42580         // can we do this more than once??
42581         
42582          // stop form submits
42583       
42584  
42585         // disable everything...
42586         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
42587         this.toolbars = {};
42588            
42589         for (var i in  ty) {
42590           
42591             this.toolbars[i] = this.buildToolbar(ty[i],i);
42592         }
42593         this.tb = this.toolbars.BODY;
42594         this.tb.el.show();
42595         this.buildFooter();
42596         this.footer.show();
42597         editor.on('hide', function( ) { this.footer.hide() }, this);
42598         editor.on('show', function( ) { this.footer.show() }, this);
42599         
42600          
42601         this.rendered = true;
42602         
42603         // the all the btns;
42604         editor.on('editorevent', this.updateToolbar, this);
42605         // other toolbars need to implement this..
42606         //editor.on('editmodechange', this.updateToolbar, this);
42607     },
42608     
42609     
42610     
42611     /**
42612      * Protected method that will not generally be called directly. It triggers
42613      * a toolbar update by reading the markup state of the current selection in the editor.
42614      */
42615     updateToolbar: function(editor,ev,sel){
42616
42617         //Roo.log(ev);
42618         // capture mouse up - this is handy for selecting images..
42619         // perhaps should go somewhere else...
42620         if(!this.editor.activated){
42621              this.editor.onFirstFocus();
42622             return;
42623         }
42624         
42625         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
42626         // selectNode - might want to handle IE?
42627         if (ev &&
42628             (ev.type == 'mouseup' || ev.type == 'click' ) &&
42629             ev.target && ev.target.tagName == 'IMG') {
42630             // they have click on an image...
42631             // let's see if we can change the selection...
42632             sel = ev.target;
42633          
42634               var nodeRange = sel.ownerDocument.createRange();
42635             try {
42636                 nodeRange.selectNode(sel);
42637             } catch (e) {
42638                 nodeRange.selectNodeContents(sel);
42639             }
42640             //nodeRange.collapse(true);
42641             var s = editor.win.getSelection();
42642             s.removeAllRanges();
42643             s.addRange(nodeRange);
42644         }  
42645         
42646       
42647         var updateFooter = sel ? false : true;
42648         
42649         
42650         var ans = this.editor.getAllAncestors();
42651         
42652         // pick
42653         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
42654         
42655         if (!sel) { 
42656             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
42657             sel = sel ? sel : this.editor.doc.body;
42658             sel = sel.tagName.length ? sel : this.editor.doc.body;
42659             
42660         }
42661         // pick a menu that exists..
42662         var tn = sel.tagName.toUpperCase();
42663         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
42664         
42665         tn = sel.tagName.toUpperCase();
42666         
42667         var lastSel = this.tb.selectedNode
42668         
42669         this.tb.selectedNode = sel;
42670         
42671         // if current menu does not match..
42672         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
42673                 
42674             this.tb.el.hide();
42675             ///console.log("show: " + tn);
42676             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
42677             this.tb.el.show();
42678             // update name
42679             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
42680             
42681             
42682             // update attributes
42683             if (this.tb.fields) {
42684                 this.tb.fields.each(function(e) {
42685                     if (e.stylename) {
42686                         e.setValue(sel.style[e.stylename]);
42687                         return;
42688                     } 
42689                    e.setValue(sel.getAttribute(e.attrname));
42690                 });
42691             }
42692             
42693             var hasStyles = false;
42694             for(var i in this.styles) {
42695                 hasStyles = true;
42696                 break;
42697             }
42698             
42699             // update styles
42700             if (hasStyles) { 
42701                 var st = this.tb.fields.item(0);
42702                 
42703                 st.store.removeAll();
42704                
42705                 
42706                 var cn = sel.className.split(/\s+/);
42707                 
42708                 var avs = [];
42709                 if (this.styles['*']) {
42710                     
42711                     Roo.each(this.styles['*'], function(v) {
42712                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
42713                     });
42714                 }
42715                 if (this.styles[tn]) { 
42716                     Roo.each(this.styles[tn], function(v) {
42717                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
42718                     });
42719                 }
42720                 
42721                 st.store.loadData(avs);
42722                 st.collapse();
42723                 st.setValue(cn);
42724             }
42725             // flag our selected Node.
42726             this.tb.selectedNode = sel;
42727            
42728            
42729             Roo.menu.MenuMgr.hideAll();
42730
42731         }
42732         
42733         if (!updateFooter) {
42734             //this.footDisp.dom.innerHTML = ''; 
42735             return;
42736         }
42737         // update the footer
42738         //
42739         var html = '';
42740         
42741         this.footerEls = ans.reverse();
42742         Roo.each(this.footerEls, function(a,i) {
42743             if (!a) { return; }
42744             html += html.length ? ' &gt; '  :  '';
42745             
42746             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
42747             
42748         });
42749        
42750         // 
42751         var sz = this.footDisp.up('td').getSize();
42752         this.footDisp.dom.style.width = (sz.width -10) + 'px';
42753         this.footDisp.dom.style.marginLeft = '5px';
42754         
42755         this.footDisp.dom.style.overflow = 'hidden';
42756         
42757         this.footDisp.dom.innerHTML = html;
42758             
42759         //this.editorsyncValue();
42760     },
42761      
42762     
42763    
42764        
42765     // private
42766     onDestroy : function(){
42767         if(this.rendered){
42768             
42769             this.tb.items.each(function(item){
42770                 if(item.menu){
42771                     item.menu.removeAll();
42772                     if(item.menu.el){
42773                         item.menu.el.destroy();
42774                     }
42775                 }
42776                 item.destroy();
42777             });
42778              
42779         }
42780     },
42781     onFirstFocus: function() {
42782         // need to do this for all the toolbars..
42783         this.tb.items.each(function(item){
42784            item.enable();
42785         });
42786     },
42787     buildToolbar: function(tlist, nm)
42788     {
42789         var editor = this.editor;
42790          // create a new element.
42791         var wdiv = editor.wrap.createChild({
42792                 tag: 'div'
42793             }, editor.wrap.dom.firstChild.nextSibling, true);
42794         
42795        
42796         var tb = new Roo.Toolbar(wdiv);
42797         // add the name..
42798         
42799         tb.add(nm+ ":&nbsp;");
42800         
42801         var styles = [];
42802         for(var i in this.styles) {
42803             styles.push(i);
42804         }
42805         
42806         // styles...
42807         if (styles && styles.length) {
42808             
42809             // this needs a multi-select checkbox...
42810             tb.addField( new Roo.form.ComboBox({
42811                 store: new Roo.data.SimpleStore({
42812                     id : 'val',
42813                     fields: ['val', 'selected'],
42814                     data : [] 
42815                 }),
42816                 name : '-roo-edit-className',
42817                 attrname : 'className',
42818                 displayField: 'val',
42819                 typeAhead: false,
42820                 mode: 'local',
42821                 editable : false,
42822                 triggerAction: 'all',
42823                 emptyText:'Select Style',
42824                 selectOnFocus:true,
42825                 width: 130,
42826                 listeners : {
42827                     'select': function(c, r, i) {
42828                         // initial support only for on class per el..
42829                         tb.selectedNode.className =  r ? r.get('val') : '';
42830                         editor.syncValue();
42831                     }
42832                 }
42833     
42834             }));
42835         }
42836         
42837         var tbc = Roo.form.HtmlEditor.ToolbarContext;
42838         var tbops = tbc.options;
42839         
42840         for (var i in tlist) {
42841             
42842             var item = tlist[i];
42843             tb.add(item.title + ":&nbsp;");
42844             
42845             
42846             //optname == used so you can configure the options available..
42847             var opts = item.opts ? item.opts : false;
42848             if (item.optname) {
42849                 opts = tbops[item.optname];
42850            
42851             }
42852             
42853             if (opts) {
42854                 // opts == pulldown..
42855                 tb.addField( new Roo.form.ComboBox({
42856                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
42857                         id : 'val',
42858                         fields: ['val', 'display'],
42859                         data : opts  
42860                     }),
42861                     name : '-roo-edit-' + i,
42862                     attrname : i,
42863                     stylename : item.style ? item.style : false,
42864                     displayField: item.displayField ? item.displayField : 'val',
42865                     valueField :  'val',
42866                     typeAhead: false,
42867                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
42868                     editable : false,
42869                     triggerAction: 'all',
42870                     emptyText:'Select',
42871                     selectOnFocus:true,
42872                     width: item.width ? item.width  : 130,
42873                     listeners : {
42874                         'select': function(c, r, i) {
42875                             if (c.stylename) {
42876                                 tb.selectedNode.style[c.stylename] =  r.get('val');
42877                                 return;
42878                             }
42879                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
42880                         }
42881                     }
42882
42883                 }));
42884                 continue;
42885                     
42886                  
42887                 
42888                 tb.addField( new Roo.form.TextField({
42889                     name: i,
42890                     width: 100,
42891                     //allowBlank:false,
42892                     value: ''
42893                 }));
42894                 continue;
42895             }
42896             tb.addField( new Roo.form.TextField({
42897                 name: '-roo-edit-' + i,
42898                 attrname : i,
42899                 
42900                 width: item.width,
42901                 //allowBlank:true,
42902                 value: '',
42903                 listeners: {
42904                     'change' : function(f, nv, ov) {
42905                         tb.selectedNode.setAttribute(f.attrname, nv);
42906                     }
42907                 }
42908             }));
42909              
42910         }
42911         tb.addFill();
42912         var _this = this;
42913         tb.addButton( {
42914             text: 'Remove Tag',
42915     
42916             listeners : {
42917                 click : function ()
42918                 {
42919                     // remove
42920                     // undo does not work.
42921                      
42922                     var sn = tb.selectedNode;
42923                     
42924                     var pn = sn.parentNode;
42925                     
42926                     var stn =  sn.childNodes[0];
42927                     var en = sn.childNodes[sn.childNodes.length - 1 ];
42928                     while (sn.childNodes.length) {
42929                         var node = sn.childNodes[0];
42930                         sn.removeChild(node);
42931                         //Roo.log(node);
42932                         pn.insertBefore(node, sn);
42933                         
42934                     }
42935                     pn.removeChild(sn);
42936                     var range = editor.createRange();
42937         
42938                     range.setStart(stn,0);
42939                     range.setEnd(en,0); //????
42940                     //range.selectNode(sel);
42941                     
42942                     
42943                     var selection = editor.getSelection();
42944                     selection.removeAllRanges();
42945                     selection.addRange(range);
42946                     
42947                     
42948                     
42949                     //_this.updateToolbar(null, null, pn);
42950                     _this.updateToolbar(null, null, null);
42951                     _this.footDisp.dom.innerHTML = ''; 
42952                 }
42953             }
42954             
42955                     
42956                 
42957             
42958         });
42959         
42960         
42961         tb.el.on('click', function(e){
42962             e.preventDefault(); // what does this do?
42963         });
42964         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
42965         tb.el.hide();
42966         tb.name = nm;
42967         // dont need to disable them... as they will get hidden
42968         return tb;
42969          
42970         
42971     },
42972     buildFooter : function()
42973     {
42974         
42975         var fel = this.editor.wrap.createChild();
42976         this.footer = new Roo.Toolbar(fel);
42977         // toolbar has scrolly on left / right?
42978         var footDisp= new Roo.Toolbar.Fill();
42979         var _t = this;
42980         this.footer.add(
42981             {
42982                 text : '&lt;',
42983                 xtype: 'Button',
42984                 handler : function() {
42985                     _t.footDisp.scrollTo('left',0,true)
42986                 }
42987             }
42988         );
42989         this.footer.add( footDisp );
42990         this.footer.add( 
42991             {
42992                 text : '&gt;',
42993                 xtype: 'Button',
42994                 handler : function() {
42995                     // no animation..
42996                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
42997                 }
42998             }
42999         );
43000         var fel = Roo.get(footDisp.el);
43001         fel.addClass('x-editor-context');
43002         this.footDispWrap = fel; 
43003         this.footDispWrap.overflow  = 'hidden';
43004         
43005         this.footDisp = fel.createChild();
43006         this.footDispWrap.on('click', this.onContextClick, this)
43007         
43008         
43009     },
43010     onContextClick : function (ev,dom)
43011     {
43012         ev.preventDefault();
43013         var  cn = dom.className;
43014         //Roo.log(cn);
43015         if (!cn.match(/x-ed-loc-/)) {
43016             return;
43017         }
43018         var n = cn.split('-').pop();
43019         var ans = this.footerEls;
43020         var sel = ans[n];
43021         
43022          // pick
43023         var range = this.editor.createRange();
43024         
43025         range.selectNodeContents(sel);
43026         //range.selectNode(sel);
43027         
43028         
43029         var selection = this.editor.getSelection();
43030         selection.removeAllRanges();
43031         selection.addRange(range);
43032         
43033         
43034         
43035         this.updateToolbar(null, null, sel);
43036         
43037         
43038     }
43039     
43040     
43041     
43042     
43043     
43044 });
43045
43046
43047
43048
43049
43050 /*
43051  * Based on:
43052  * Ext JS Library 1.1.1
43053  * Copyright(c) 2006-2007, Ext JS, LLC.
43054  *
43055  * Originally Released Under LGPL - original licence link has changed is not relivant.
43056  *
43057  * Fork - LGPL
43058  * <script type="text/javascript">
43059  */
43060  
43061 /**
43062  * @class Roo.form.BasicForm
43063  * @extends Roo.util.Observable
43064  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
43065  * @constructor
43066  * @param {String/HTMLElement/Roo.Element} el The form element or its id
43067  * @param {Object} config Configuration options
43068  */
43069 Roo.form.BasicForm = function(el, config){
43070     this.allItems = [];
43071     this.childForms = [];
43072     Roo.apply(this, config);
43073     /*
43074      * The Roo.form.Field items in this form.
43075      * @type MixedCollection
43076      */
43077      
43078      
43079     this.items = new Roo.util.MixedCollection(false, function(o){
43080         return o.id || (o.id = Roo.id());
43081     });
43082     this.addEvents({
43083         /**
43084          * @event beforeaction
43085          * Fires before any action is performed. Return false to cancel the action.
43086          * @param {Form} this
43087          * @param {Action} action The action to be performed
43088          */
43089         beforeaction: true,
43090         /**
43091          * @event actionfailed
43092          * Fires when an action fails.
43093          * @param {Form} this
43094          * @param {Action} action The action that failed
43095          */
43096         actionfailed : true,
43097         /**
43098          * @event actioncomplete
43099          * Fires when an action is completed.
43100          * @param {Form} this
43101          * @param {Action} action The action that completed
43102          */
43103         actioncomplete : true
43104     });
43105     if(el){
43106         this.initEl(el);
43107     }
43108     Roo.form.BasicForm.superclass.constructor.call(this);
43109 };
43110
43111 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
43112     /**
43113      * @cfg {String} method
43114      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
43115      */
43116     /**
43117      * @cfg {DataReader} reader
43118      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
43119      * This is optional as there is built-in support for processing JSON.
43120      */
43121     /**
43122      * @cfg {DataReader} errorReader
43123      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
43124      * This is completely optional as there is built-in support for processing JSON.
43125      */
43126     /**
43127      * @cfg {String} url
43128      * The URL to use for form actions if one isn't supplied in the action options.
43129      */
43130     /**
43131      * @cfg {Boolean} fileUpload
43132      * Set to true if this form is a file upload.
43133      */
43134      
43135     /**
43136      * @cfg {Object} baseParams
43137      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
43138      */
43139      /**
43140      
43141     /**
43142      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
43143      */
43144     timeout: 30,
43145
43146     // private
43147     activeAction : null,
43148
43149     /**
43150      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
43151      * or setValues() data instead of when the form was first created.
43152      */
43153     trackResetOnLoad : false,
43154     
43155     
43156     /**
43157      * childForms - used for multi-tab forms
43158      * @type {Array}
43159      */
43160     childForms : false,
43161     
43162     /**
43163      * allItems - full list of fields.
43164      * @type {Array}
43165      */
43166     allItems : false,
43167     
43168     /**
43169      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
43170      * element by passing it or its id or mask the form itself by passing in true.
43171      * @type Mixed
43172      */
43173     waitMsgTarget : false,
43174
43175     // private
43176     initEl : function(el){
43177         this.el = Roo.get(el);
43178         this.id = this.el.id || Roo.id();
43179         this.el.on('submit', this.onSubmit, this);
43180         this.el.addClass('x-form');
43181     },
43182
43183     // private
43184     onSubmit : function(e){
43185         e.stopEvent();
43186     },
43187
43188     /**
43189      * Returns true if client-side validation on the form is successful.
43190      * @return Boolean
43191      */
43192     isValid : function(){
43193         var valid = true;
43194         this.items.each(function(f){
43195            if(!f.validate()){
43196                valid = false;
43197            }
43198         });
43199         return valid;
43200     },
43201
43202     /**
43203      * Returns true if any fields in this form have changed since their original load.
43204      * @return Boolean
43205      */
43206     isDirty : function(){
43207         var dirty = false;
43208         this.items.each(function(f){
43209            if(f.isDirty()){
43210                dirty = true;
43211                return false;
43212            }
43213         });
43214         return dirty;
43215     },
43216
43217     /**
43218      * Performs a predefined action (submit or load) or custom actions you define on this form.
43219      * @param {String} actionName The name of the action type
43220      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
43221      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
43222      * accept other config options):
43223      * <pre>
43224 Property          Type             Description
43225 ----------------  ---------------  ----------------------------------------------------------------------------------
43226 url               String           The url for the action (defaults to the form's url)
43227 method            String           The form method to use (defaults to the form's method, or POST if not defined)
43228 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
43229 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
43230                                    validate the form on the client (defaults to false)
43231      * </pre>
43232      * @return {BasicForm} this
43233      */
43234     doAction : function(action, options){
43235         if(typeof action == 'string'){
43236             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
43237         }
43238         if(this.fireEvent('beforeaction', this, action) !== false){
43239             this.beforeAction(action);
43240             action.run.defer(100, action);
43241         }
43242         return this;
43243     },
43244
43245     /**
43246      * Shortcut to do a submit action.
43247      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43248      * @return {BasicForm} this
43249      */
43250     submit : function(options){
43251         this.doAction('submit', options);
43252         return this;
43253     },
43254
43255     /**
43256      * Shortcut to do a load action.
43257      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43258      * @return {BasicForm} this
43259      */
43260     load : function(options){
43261         this.doAction('load', options);
43262         return this;
43263     },
43264
43265     /**
43266      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
43267      * @param {Record} record The record to edit
43268      * @return {BasicForm} this
43269      */
43270     updateRecord : function(record){
43271         record.beginEdit();
43272         var fs = record.fields;
43273         fs.each(function(f){
43274             var field = this.findField(f.name);
43275             if(field){
43276                 record.set(f.name, field.getValue());
43277             }
43278         }, this);
43279         record.endEdit();
43280         return this;
43281     },
43282
43283     /**
43284      * Loads an Roo.data.Record into this form.
43285      * @param {Record} record The record to load
43286      * @return {BasicForm} this
43287      */
43288     loadRecord : function(record){
43289         this.setValues(record.data);
43290         return this;
43291     },
43292
43293     // private
43294     beforeAction : function(action){
43295         var o = action.options;
43296         
43297        
43298         if(this.waitMsgTarget === true){
43299             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
43300         }else if(this.waitMsgTarget){
43301             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
43302             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
43303         }else {
43304             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
43305         }
43306          
43307     },
43308
43309     // private
43310     afterAction : function(action, success){
43311         this.activeAction = null;
43312         var o = action.options;
43313         
43314         if(this.waitMsgTarget === true){
43315             this.el.unmask();
43316         }else if(this.waitMsgTarget){
43317             this.waitMsgTarget.unmask();
43318         }else{
43319             Roo.MessageBox.updateProgress(1);
43320             Roo.MessageBox.hide();
43321         }
43322          
43323         if(success){
43324             if(o.reset){
43325                 this.reset();
43326             }
43327             Roo.callback(o.success, o.scope, [this, action]);
43328             this.fireEvent('actioncomplete', this, action);
43329             
43330         }else{
43331             
43332             // failure condition..
43333             // we have a scenario where updates need confirming.
43334             // eg. if a locking scenario exists..
43335             // we look for { errors : { needs_confirm : true }} in the response.
43336             if (
43337                 (typeof(action.result) != 'undefined')  &&
43338                 (typeof(action.result.errors) != 'undefined')  &&
43339                 (typeof(action.result.errors.needs_confirm) != 'undefined')
43340            ){
43341                 var _t = this;
43342                 Roo.MessageBox.confirm(
43343                     "Change requires confirmation",
43344                     action.result.errorMsg,
43345                     function(r) {
43346                         if (r != 'yes') {
43347                             return;
43348                         }
43349                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
43350                     }
43351                     
43352                 );
43353                 
43354                 
43355                 
43356                 return;
43357             }
43358             
43359             Roo.callback(o.failure, o.scope, [this, action]);
43360             // show an error message if no failed handler is set..
43361             if (!this.hasListener('actionfailed')) {
43362                 Roo.MessageBox.alert("Error",
43363                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
43364                         action.result.errorMsg :
43365                         "Saving Failed, please check your entries or try again"
43366                 );
43367             }
43368             
43369             this.fireEvent('actionfailed', this, action);
43370         }
43371         
43372     },
43373
43374     /**
43375      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
43376      * @param {String} id The value to search for
43377      * @return Field
43378      */
43379     findField : function(id){
43380         var field = this.items.get(id);
43381         if(!field){
43382             this.items.each(function(f){
43383                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
43384                     field = f;
43385                     return false;
43386                 }
43387             });
43388         }
43389         return field || null;
43390     },
43391
43392     /**
43393      * Add a secondary form to this one, 
43394      * Used to provide tabbed forms. One form is primary, with hidden values 
43395      * which mirror the elements from the other forms.
43396      * 
43397      * @param {Roo.form.Form} form to add.
43398      * 
43399      */
43400     addForm : function(form)
43401     {
43402        
43403         if (this.childForms.indexOf(form) > -1) {
43404             // already added..
43405             return;
43406         }
43407         this.childForms.push(form);
43408         var n = '';
43409         Roo.each(form.allItems, function (fe) {
43410             
43411             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
43412             if (this.findField(n)) { // already added..
43413                 return;
43414             }
43415             var add = new Roo.form.Hidden({
43416                 name : n
43417             });
43418             add.render(this.el);
43419             
43420             this.add( add );
43421         }, this);
43422         
43423     },
43424     /**
43425      * Mark fields in this form invalid in bulk.
43426      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
43427      * @return {BasicForm} this
43428      */
43429     markInvalid : function(errors){
43430         if(errors instanceof Array){
43431             for(var i = 0, len = errors.length; i < len; i++){
43432                 var fieldError = errors[i];
43433                 var f = this.findField(fieldError.id);
43434                 if(f){
43435                     f.markInvalid(fieldError.msg);
43436                 }
43437             }
43438         }else{
43439             var field, id;
43440             for(id in errors){
43441                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
43442                     field.markInvalid(errors[id]);
43443                 }
43444             }
43445         }
43446         Roo.each(this.childForms || [], function (f) {
43447             f.markInvalid(errors);
43448         });
43449         
43450         return this;
43451     },
43452
43453     /**
43454      * Set values for fields in this form in bulk.
43455      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
43456      * @return {BasicForm} this
43457      */
43458     setValues : function(values){
43459         if(values instanceof Array){ // array of objects
43460             for(var i = 0, len = values.length; i < len; i++){
43461                 var v = values[i];
43462                 var f = this.findField(v.id);
43463                 if(f){
43464                     f.setValue(v.value);
43465                     if(this.trackResetOnLoad){
43466                         f.originalValue = f.getValue();
43467                     }
43468                 }
43469             }
43470         }else{ // object hash
43471             var field, id;
43472             for(id in values){
43473                 if(typeof values[id] != 'function' && (field = this.findField(id))){
43474                     
43475                     if (field.setFromData && 
43476                         field.valueField && 
43477                         field.displayField &&
43478                         // combos' with local stores can 
43479                         // be queried via setValue()
43480                         // to set their value..
43481                         (field.store && !field.store.isLocal)
43482                         ) {
43483                         // it's a combo
43484                         var sd = { };
43485                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
43486                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
43487                         field.setFromData(sd);
43488                         
43489                     } else {
43490                         field.setValue(values[id]);
43491                     }
43492                     
43493                     
43494                     if(this.trackResetOnLoad){
43495                         field.originalValue = field.getValue();
43496                     }
43497                 }
43498             }
43499         }
43500          
43501         Roo.each(this.childForms || [], function (f) {
43502             f.setValues(values);
43503         });
43504                 
43505         return this;
43506     },
43507
43508     /**
43509      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
43510      * they are returned as an array.
43511      * @param {Boolean} asString
43512      * @return {Object}
43513      */
43514     getValues : function(asString){
43515         if (this.childForms) {
43516             // copy values from the child forms
43517             Roo.each(this.childForms, function (f) {
43518                 this.setValues(f.getValues());
43519             }, this);
43520         }
43521         
43522         
43523         
43524         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
43525         if(asString === true){
43526             return fs;
43527         }
43528         return Roo.urlDecode(fs);
43529     },
43530     
43531     /**
43532      * Returns the fields in this form as an object with key/value pairs. 
43533      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
43534      * @return {Object}
43535      */
43536     getFieldValues : function(with_hidden)
43537     {
43538         if (this.childForms) {
43539             // copy values from the child forms
43540             // should this call getFieldValues - probably not as we do not currently copy
43541             // hidden fields when we generate..
43542             Roo.each(this.childForms, function (f) {
43543                 this.setValues(f.getValues());
43544             }, this);
43545         }
43546         
43547         var ret = {};
43548         this.items.each(function(f){
43549             if (!f.getName()) {
43550                 return;
43551             }
43552             var v = f.getValue();
43553             if (f.inputType =='radio') {
43554                 if (typeof(ret[f.getName()]) == 'undefined') {
43555                     ret[f.getName()] = ''; // empty..
43556                 }
43557                 
43558                 if (!f.el.dom.checked) {
43559                     return;
43560                     
43561                 }
43562                 v = f.el.dom.value;
43563                 
43564             }
43565             
43566             // not sure if this supported any more..
43567             if ((typeof(v) == 'object') && f.getRawValue) {
43568                 v = f.getRawValue() ; // dates..
43569             }
43570             // combo boxes where name != hiddenName...
43571             if (f.name != f.getName()) {
43572                 ret[f.name] = f.getRawValue();
43573             }
43574             ret[f.getName()] = v;
43575         });
43576         
43577         return ret;
43578     },
43579
43580     /**
43581      * Clears all invalid messages in this form.
43582      * @return {BasicForm} this
43583      */
43584     clearInvalid : function(){
43585         this.items.each(function(f){
43586            f.clearInvalid();
43587         });
43588         
43589         Roo.each(this.childForms || [], function (f) {
43590             f.clearInvalid();
43591         });
43592         
43593         
43594         return this;
43595     },
43596
43597     /**
43598      * Resets this form.
43599      * @return {BasicForm} this
43600      */
43601     reset : function(){
43602         this.items.each(function(f){
43603             f.reset();
43604         });
43605         
43606         Roo.each(this.childForms || [], function (f) {
43607             f.reset();
43608         });
43609        
43610         
43611         return this;
43612     },
43613
43614     /**
43615      * Add Roo.form components to this form.
43616      * @param {Field} field1
43617      * @param {Field} field2 (optional)
43618      * @param {Field} etc (optional)
43619      * @return {BasicForm} this
43620      */
43621     add : function(){
43622         this.items.addAll(Array.prototype.slice.call(arguments, 0));
43623         return this;
43624     },
43625
43626
43627     /**
43628      * Removes a field from the items collection (does NOT remove its markup).
43629      * @param {Field} field
43630      * @return {BasicForm} this
43631      */
43632     remove : function(field){
43633         this.items.remove(field);
43634         return this;
43635     },
43636
43637     /**
43638      * Looks at the fields in this form, checks them for an id attribute,
43639      * and calls applyTo on the existing dom element with that id.
43640      * @return {BasicForm} this
43641      */
43642     render : function(){
43643         this.items.each(function(f){
43644             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
43645                 f.applyTo(f.id);
43646             }
43647         });
43648         return this;
43649     },
43650
43651     /**
43652      * Calls {@link Ext#apply} for all fields in this form with the passed object.
43653      * @param {Object} values
43654      * @return {BasicForm} this
43655      */
43656     applyToFields : function(o){
43657         this.items.each(function(f){
43658            Roo.apply(f, o);
43659         });
43660         return this;
43661     },
43662
43663     /**
43664      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
43665      * @param {Object} values
43666      * @return {BasicForm} this
43667      */
43668     applyIfToFields : function(o){
43669         this.items.each(function(f){
43670            Roo.applyIf(f, o);
43671         });
43672         return this;
43673     }
43674 });
43675
43676 // back compat
43677 Roo.BasicForm = Roo.form.BasicForm;/*
43678  * Based on:
43679  * Ext JS Library 1.1.1
43680  * Copyright(c) 2006-2007, Ext JS, LLC.
43681  *
43682  * Originally Released Under LGPL - original licence link has changed is not relivant.
43683  *
43684  * Fork - LGPL
43685  * <script type="text/javascript">
43686  */
43687
43688 /**
43689  * @class Roo.form.Form
43690  * @extends Roo.form.BasicForm
43691  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
43692  * @constructor
43693  * @param {Object} config Configuration options
43694  */
43695 Roo.form.Form = function(config){
43696     var xitems =  [];
43697     if (config.items) {
43698         xitems = config.items;
43699         delete config.items;
43700     }
43701    
43702     
43703     Roo.form.Form.superclass.constructor.call(this, null, config);
43704     this.url = this.url || this.action;
43705     if(!this.root){
43706         this.root = new Roo.form.Layout(Roo.applyIf({
43707             id: Roo.id()
43708         }, config));
43709     }
43710     this.active = this.root;
43711     /**
43712      * Array of all the buttons that have been added to this form via {@link addButton}
43713      * @type Array
43714      */
43715     this.buttons = [];
43716     this.allItems = [];
43717     this.addEvents({
43718         /**
43719          * @event clientvalidation
43720          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
43721          * @param {Form} this
43722          * @param {Boolean} valid true if the form has passed client-side validation
43723          */
43724         clientvalidation: true,
43725         /**
43726          * @event rendered
43727          * Fires when the form is rendered
43728          * @param {Roo.form.Form} form
43729          */
43730         rendered : true
43731     });
43732     
43733     if (this.progressUrl) {
43734             // push a hidden field onto the list of fields..
43735             this.addxtype( {
43736                     xns: Roo.form, 
43737                     xtype : 'Hidden', 
43738                     name : 'UPLOAD_IDENTIFIER' 
43739             });
43740         }
43741         
43742     
43743     Roo.each(xitems, this.addxtype, this);
43744     
43745     
43746     
43747 };
43748
43749 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
43750     /**
43751      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
43752      */
43753     /**
43754      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
43755      */
43756     /**
43757      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
43758      */
43759     buttonAlign:'center',
43760
43761     /**
43762      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
43763      */
43764     minButtonWidth:75,
43765
43766     /**
43767      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
43768      * This property cascades to child containers if not set.
43769      */
43770     labelAlign:'left',
43771
43772     /**
43773      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
43774      * fires a looping event with that state. This is required to bind buttons to the valid
43775      * state using the config value formBind:true on the button.
43776      */
43777     monitorValid : false,
43778
43779     /**
43780      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
43781      */
43782     monitorPoll : 200,
43783     
43784     /**
43785      * @cfg {String} progressUrl - Url to return progress data 
43786      */
43787     
43788     progressUrl : false,
43789   
43790     /**
43791      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
43792      * fields are added and the column is closed. If no fields are passed the column remains open
43793      * until end() is called.
43794      * @param {Object} config The config to pass to the column
43795      * @param {Field} field1 (optional)
43796      * @param {Field} field2 (optional)
43797      * @param {Field} etc (optional)
43798      * @return Column The column container object
43799      */
43800     column : function(c){
43801         var col = new Roo.form.Column(c);
43802         this.start(col);
43803         if(arguments.length > 1){ // duplicate code required because of Opera
43804             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43805             this.end();
43806         }
43807         return col;
43808     },
43809
43810     /**
43811      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
43812      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
43813      * until end() is called.
43814      * @param {Object} config The config to pass to the fieldset
43815      * @param {Field} field1 (optional)
43816      * @param {Field} field2 (optional)
43817      * @param {Field} etc (optional)
43818      * @return FieldSet The fieldset container object
43819      */
43820     fieldset : function(c){
43821         var fs = new Roo.form.FieldSet(c);
43822         this.start(fs);
43823         if(arguments.length > 1){ // duplicate code required because of Opera
43824             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43825             this.end();
43826         }
43827         return fs;
43828     },
43829
43830     /**
43831      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
43832      * fields are added and the container is closed. If no fields are passed the container remains open
43833      * until end() is called.
43834      * @param {Object} config The config to pass to the Layout
43835      * @param {Field} field1 (optional)
43836      * @param {Field} field2 (optional)
43837      * @param {Field} etc (optional)
43838      * @return Layout The container object
43839      */
43840     container : function(c){
43841         var l = new Roo.form.Layout(c);
43842         this.start(l);
43843         if(arguments.length > 1){ // duplicate code required because of Opera
43844             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43845             this.end();
43846         }
43847         return l;
43848     },
43849
43850     /**
43851      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
43852      * @param {Object} container A Roo.form.Layout or subclass of Layout
43853      * @return {Form} this
43854      */
43855     start : function(c){
43856         // cascade label info
43857         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
43858         this.active.stack.push(c);
43859         c.ownerCt = this.active;
43860         this.active = c;
43861         return this;
43862     },
43863
43864     /**
43865      * Closes the current open container
43866      * @return {Form} this
43867      */
43868     end : function(){
43869         if(this.active == this.root){
43870             return this;
43871         }
43872         this.active = this.active.ownerCt;
43873         return this;
43874     },
43875
43876     /**
43877      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
43878      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
43879      * as the label of the field.
43880      * @param {Field} field1
43881      * @param {Field} field2 (optional)
43882      * @param {Field} etc. (optional)
43883      * @return {Form} this
43884      */
43885     add : function(){
43886         this.active.stack.push.apply(this.active.stack, arguments);
43887         this.allItems.push.apply(this.allItems,arguments);
43888         var r = [];
43889         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
43890             if(a[i].isFormField){
43891                 r.push(a[i]);
43892             }
43893         }
43894         if(r.length > 0){
43895             Roo.form.Form.superclass.add.apply(this, r);
43896         }
43897         return this;
43898     },
43899     
43900
43901     
43902     
43903     
43904      /**
43905      * Find any element that has been added to a form, using it's ID or name
43906      * This can include framesets, columns etc. along with regular fields..
43907      * @param {String} id - id or name to find.
43908      
43909      * @return {Element} e - or false if nothing found.
43910      */
43911     findbyId : function(id)
43912     {
43913         var ret = false;
43914         if (!id) {
43915             return ret;
43916         }
43917         Roo.each(this.allItems, function(f){
43918             if (f.id == id || f.name == id ){
43919                 ret = f;
43920                 return false;
43921             }
43922         });
43923         return ret;
43924     },
43925
43926     
43927     
43928     /**
43929      * Render this form into the passed container. This should only be called once!
43930      * @param {String/HTMLElement/Element} container The element this component should be rendered into
43931      * @return {Form} this
43932      */
43933     render : function(ct)
43934     {
43935         
43936         
43937         
43938         ct = Roo.get(ct);
43939         var o = this.autoCreate || {
43940             tag: 'form',
43941             method : this.method || 'POST',
43942             id : this.id || Roo.id()
43943         };
43944         this.initEl(ct.createChild(o));
43945
43946         this.root.render(this.el);
43947         
43948        
43949              
43950         this.items.each(function(f){
43951             f.render('x-form-el-'+f.id);
43952         });
43953
43954         if(this.buttons.length > 0){
43955             // tables are required to maintain order and for correct IE layout
43956             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
43957                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
43958                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
43959             }}, null, true);
43960             var tr = tb.getElementsByTagName('tr')[0];
43961             for(var i = 0, len = this.buttons.length; i < len; i++) {
43962                 var b = this.buttons[i];
43963                 var td = document.createElement('td');
43964                 td.className = 'x-form-btn-td';
43965                 b.render(tr.appendChild(td));
43966             }
43967         }
43968         if(this.monitorValid){ // initialize after render
43969             this.startMonitoring();
43970         }
43971         this.fireEvent('rendered', this);
43972         return this;
43973     },
43974
43975     /**
43976      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
43977      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
43978      * object or a valid Roo.DomHelper element config
43979      * @param {Function} handler The function called when the button is clicked
43980      * @param {Object} scope (optional) The scope of the handler function
43981      * @return {Roo.Button}
43982      */
43983     addButton : function(config, handler, scope){
43984         var bc = {
43985             handler: handler,
43986             scope: scope,
43987             minWidth: this.minButtonWidth,
43988             hideParent:true
43989         };
43990         if(typeof config == "string"){
43991             bc.text = config;
43992         }else{
43993             Roo.apply(bc, config);
43994         }
43995         var btn = new Roo.Button(null, bc);
43996         this.buttons.push(btn);
43997         return btn;
43998     },
43999
44000      /**
44001      * Adds a series of form elements (using the xtype property as the factory method.
44002      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
44003      * @param {Object} config 
44004      */
44005     
44006     addxtype : function()
44007     {
44008         var ar = Array.prototype.slice.call(arguments, 0);
44009         var ret = false;
44010         for(var i = 0; i < ar.length; i++) {
44011             if (!ar[i]) {
44012                 continue; // skip -- if this happends something invalid got sent, we 
44013                 // should ignore it, as basically that interface element will not show up
44014                 // and that should be pretty obvious!!
44015             }
44016             
44017             if (Roo.form[ar[i].xtype]) {
44018                 ar[i].form = this;
44019                 var fe = Roo.factory(ar[i], Roo.form);
44020                 if (!ret) {
44021                     ret = fe;
44022                 }
44023                 fe.form = this;
44024                 if (fe.store) {
44025                     fe.store.form = this;
44026                 }
44027                 if (fe.isLayout) {  
44028                          
44029                     this.start(fe);
44030                     this.allItems.push(fe);
44031                     if (fe.items && fe.addxtype) {
44032                         fe.addxtype.apply(fe, fe.items);
44033                         delete fe.items;
44034                     }
44035                      this.end();
44036                     continue;
44037                 }
44038                 
44039                 
44040                  
44041                 this.add(fe);
44042               //  console.log('adding ' + ar[i].xtype);
44043             }
44044             if (ar[i].xtype == 'Button') {  
44045                 //console.log('adding button');
44046                 //console.log(ar[i]);
44047                 this.addButton(ar[i]);
44048                 this.allItems.push(fe);
44049                 continue;
44050             }
44051             
44052             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
44053                 alert('end is not supported on xtype any more, use items');
44054             //    this.end();
44055             //    //console.log('adding end');
44056             }
44057             
44058         }
44059         return ret;
44060     },
44061     
44062     /**
44063      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
44064      * option "monitorValid"
44065      */
44066     startMonitoring : function(){
44067         if(!this.bound){
44068             this.bound = true;
44069             Roo.TaskMgr.start({
44070                 run : this.bindHandler,
44071                 interval : this.monitorPoll || 200,
44072                 scope: this
44073             });
44074         }
44075     },
44076
44077     /**
44078      * Stops monitoring of the valid state of this form
44079      */
44080     stopMonitoring : function(){
44081         this.bound = false;
44082     },
44083
44084     // private
44085     bindHandler : function(){
44086         if(!this.bound){
44087             return false; // stops binding
44088         }
44089         var valid = true;
44090         this.items.each(function(f){
44091             if(!f.isValid(true)){
44092                 valid = false;
44093                 return false;
44094             }
44095         });
44096         for(var i = 0, len = this.buttons.length; i < len; i++){
44097             var btn = this.buttons[i];
44098             if(btn.formBind === true && btn.disabled === valid){
44099                 btn.setDisabled(!valid);
44100             }
44101         }
44102         this.fireEvent('clientvalidation', this, valid);
44103     }
44104     
44105     
44106     
44107     
44108     
44109     
44110     
44111     
44112 });
44113
44114
44115 // back compat
44116 Roo.Form = Roo.form.Form;
44117 /*
44118  * Based on:
44119  * Ext JS Library 1.1.1
44120  * Copyright(c) 2006-2007, Ext JS, LLC.
44121  *
44122  * Originally Released Under LGPL - original licence link has changed is not relivant.
44123  *
44124  * Fork - LGPL
44125  * <script type="text/javascript">
44126  */
44127  
44128  /**
44129  * @class Roo.form.Action
44130  * Internal Class used to handle form actions
44131  * @constructor
44132  * @param {Roo.form.BasicForm} el The form element or its id
44133  * @param {Object} config Configuration options
44134  */
44135  
44136  
44137 // define the action interface
44138 Roo.form.Action = function(form, options){
44139     this.form = form;
44140     this.options = options || {};
44141 };
44142 /**
44143  * Client Validation Failed
44144  * @const 
44145  */
44146 Roo.form.Action.CLIENT_INVALID = 'client';
44147 /**
44148  * Server Validation Failed
44149  * @const 
44150  */
44151  Roo.form.Action.SERVER_INVALID = 'server';
44152  /**
44153  * Connect to Server Failed
44154  * @const 
44155  */
44156 Roo.form.Action.CONNECT_FAILURE = 'connect';
44157 /**
44158  * Reading Data from Server Failed
44159  * @const 
44160  */
44161 Roo.form.Action.LOAD_FAILURE = 'load';
44162
44163 Roo.form.Action.prototype = {
44164     type : 'default',
44165     failureType : undefined,
44166     response : undefined,
44167     result : undefined,
44168
44169     // interface method
44170     run : function(options){
44171
44172     },
44173
44174     // interface method
44175     success : function(response){
44176
44177     },
44178
44179     // interface method
44180     handleResponse : function(response){
44181
44182     },
44183
44184     // default connection failure
44185     failure : function(response){
44186         
44187         this.response = response;
44188         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44189         this.form.afterAction(this, false);
44190     },
44191
44192     processResponse : function(response){
44193         this.response = response;
44194         if(!response.responseText){
44195             return true;
44196         }
44197         this.result = this.handleResponse(response);
44198         return this.result;
44199     },
44200
44201     // utility functions used internally
44202     getUrl : function(appendParams){
44203         var url = this.options.url || this.form.url || this.form.el.dom.action;
44204         if(appendParams){
44205             var p = this.getParams();
44206             if(p){
44207                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
44208             }
44209         }
44210         return url;
44211     },
44212
44213     getMethod : function(){
44214         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
44215     },
44216
44217     getParams : function(){
44218         var bp = this.form.baseParams;
44219         var p = this.options.params;
44220         if(p){
44221             if(typeof p == "object"){
44222                 p = Roo.urlEncode(Roo.applyIf(p, bp));
44223             }else if(typeof p == 'string' && bp){
44224                 p += '&' + Roo.urlEncode(bp);
44225             }
44226         }else if(bp){
44227             p = Roo.urlEncode(bp);
44228         }
44229         return p;
44230     },
44231
44232     createCallback : function(){
44233         return {
44234             success: this.success,
44235             failure: this.failure,
44236             scope: this,
44237             timeout: (this.form.timeout*1000),
44238             upload: this.form.fileUpload ? this.success : undefined
44239         };
44240     }
44241 };
44242
44243 Roo.form.Action.Submit = function(form, options){
44244     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
44245 };
44246
44247 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
44248     type : 'submit',
44249
44250     haveProgress : false,
44251     uploadComplete : false,
44252     
44253     // uploadProgress indicator.
44254     uploadProgress : function()
44255     {
44256         if (!this.form.progressUrl) {
44257             return;
44258         }
44259         
44260         if (!this.haveProgress) {
44261             Roo.MessageBox.progress("Uploading", "Uploading");
44262         }
44263         if (this.uploadComplete) {
44264            Roo.MessageBox.hide();
44265            return;
44266         }
44267         
44268         this.haveProgress = true;
44269    
44270         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
44271         
44272         var c = new Roo.data.Connection();
44273         c.request({
44274             url : this.form.progressUrl,
44275             params: {
44276                 id : uid
44277             },
44278             method: 'GET',
44279             success : function(req){
44280                //console.log(data);
44281                 var rdata = false;
44282                 var edata;
44283                 try  {
44284                    rdata = Roo.decode(req.responseText)
44285                 } catch (e) {
44286                     Roo.log("Invalid data from server..");
44287                     Roo.log(edata);
44288                     return;
44289                 }
44290                 if (!rdata || !rdata.success) {
44291                     Roo.log(rdata);
44292                     Roo.MessageBox.alert(Roo.encode(rdata));
44293                     return;
44294                 }
44295                 var data = rdata.data;
44296                 
44297                 if (this.uploadComplete) {
44298                    Roo.MessageBox.hide();
44299                    return;
44300                 }
44301                    
44302                 if (data){
44303                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
44304                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
44305                     );
44306                 }
44307                 this.uploadProgress.defer(2000,this);
44308             },
44309        
44310             failure: function(data) {
44311                 Roo.log('progress url failed ');
44312                 Roo.log(data);
44313             },
44314             scope : this
44315         });
44316            
44317     },
44318     
44319     
44320     run : function()
44321     {
44322         // run get Values on the form, so it syncs any secondary forms.
44323         this.form.getValues();
44324         
44325         var o = this.options;
44326         var method = this.getMethod();
44327         var isPost = method == 'POST';
44328         if(o.clientValidation === false || this.form.isValid()){
44329             
44330             if (this.form.progressUrl) {
44331                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
44332                     (new Date() * 1) + '' + Math.random());
44333                     
44334             } 
44335             
44336             
44337             Roo.Ajax.request(Roo.apply(this.createCallback(), {
44338                 form:this.form.el.dom,
44339                 url:this.getUrl(!isPost),
44340                 method: method,
44341                 params:isPost ? this.getParams() : null,
44342                 isUpload: this.form.fileUpload
44343             }));
44344             
44345             this.uploadProgress();
44346
44347         }else if (o.clientValidation !== false){ // client validation failed
44348             this.failureType = Roo.form.Action.CLIENT_INVALID;
44349             this.form.afterAction(this, false);
44350         }
44351     },
44352
44353     success : function(response)
44354     {
44355         this.uploadComplete= true;
44356         if (this.haveProgress) {
44357             Roo.MessageBox.hide();
44358         }
44359         
44360         
44361         var result = this.processResponse(response);
44362         if(result === true || result.success){
44363             this.form.afterAction(this, true);
44364             return;
44365         }
44366         if(result.errors){
44367             this.form.markInvalid(result.errors);
44368             this.failureType = Roo.form.Action.SERVER_INVALID;
44369         }
44370         this.form.afterAction(this, false);
44371     },
44372     failure : function(response)
44373     {
44374         this.uploadComplete= true;
44375         if (this.haveProgress) {
44376             Roo.MessageBox.hide();
44377         }
44378         
44379         this.response = response;
44380         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44381         this.form.afterAction(this, false);
44382     },
44383     
44384     handleResponse : function(response){
44385         if(this.form.errorReader){
44386             var rs = this.form.errorReader.read(response);
44387             var errors = [];
44388             if(rs.records){
44389                 for(var i = 0, len = rs.records.length; i < len; i++) {
44390                     var r = rs.records[i];
44391                     errors[i] = r.data;
44392                 }
44393             }
44394             if(errors.length < 1){
44395                 errors = null;
44396             }
44397             return {
44398                 success : rs.success,
44399                 errors : errors
44400             };
44401         }
44402         var ret = false;
44403         try {
44404             ret = Roo.decode(response.responseText);
44405         } catch (e) {
44406             ret = {
44407                 success: false,
44408                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
44409                 errors : []
44410             };
44411         }
44412         return ret;
44413         
44414     }
44415 });
44416
44417
44418 Roo.form.Action.Load = function(form, options){
44419     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
44420     this.reader = this.form.reader;
44421 };
44422
44423 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
44424     type : 'load',
44425
44426     run : function(){
44427         
44428         Roo.Ajax.request(Roo.apply(
44429                 this.createCallback(), {
44430                     method:this.getMethod(),
44431                     url:this.getUrl(false),
44432                     params:this.getParams()
44433         }));
44434     },
44435
44436     success : function(response){
44437         
44438         var result = this.processResponse(response);
44439         if(result === true || !result.success || !result.data){
44440             this.failureType = Roo.form.Action.LOAD_FAILURE;
44441             this.form.afterAction(this, false);
44442             return;
44443         }
44444         this.form.clearInvalid();
44445         this.form.setValues(result.data);
44446         this.form.afterAction(this, true);
44447     },
44448
44449     handleResponse : function(response){
44450         if(this.form.reader){
44451             var rs = this.form.reader.read(response);
44452             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
44453             return {
44454                 success : rs.success,
44455                 data : data
44456             };
44457         }
44458         return Roo.decode(response.responseText);
44459     }
44460 });
44461
44462 Roo.form.Action.ACTION_TYPES = {
44463     'load' : Roo.form.Action.Load,
44464     'submit' : Roo.form.Action.Submit
44465 };/*
44466  * Based on:
44467  * Ext JS Library 1.1.1
44468  * Copyright(c) 2006-2007, Ext JS, LLC.
44469  *
44470  * Originally Released Under LGPL - original licence link has changed is not relivant.
44471  *
44472  * Fork - LGPL
44473  * <script type="text/javascript">
44474  */
44475  
44476 /**
44477  * @class Roo.form.Layout
44478  * @extends Roo.Component
44479  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
44480  * @constructor
44481  * @param {Object} config Configuration options
44482  */
44483 Roo.form.Layout = function(config){
44484     var xitems = [];
44485     if (config.items) {
44486         xitems = config.items;
44487         delete config.items;
44488     }
44489     Roo.form.Layout.superclass.constructor.call(this, config);
44490     this.stack = [];
44491     Roo.each(xitems, this.addxtype, this);
44492      
44493 };
44494
44495 Roo.extend(Roo.form.Layout, Roo.Component, {
44496     /**
44497      * @cfg {String/Object} autoCreate
44498      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
44499      */
44500     /**
44501      * @cfg {String/Object/Function} style
44502      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
44503      * a function which returns such a specification.
44504      */
44505     /**
44506      * @cfg {String} labelAlign
44507      * Valid values are "left," "top" and "right" (defaults to "left")
44508      */
44509     /**
44510      * @cfg {Number} labelWidth
44511      * Fixed width in pixels of all field labels (defaults to undefined)
44512      */
44513     /**
44514      * @cfg {Boolean} clear
44515      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
44516      */
44517     clear : true,
44518     /**
44519      * @cfg {String} labelSeparator
44520      * The separator to use after field labels (defaults to ':')
44521      */
44522     labelSeparator : ':',
44523     /**
44524      * @cfg {Boolean} hideLabels
44525      * True to suppress the display of field labels in this layout (defaults to false)
44526      */
44527     hideLabels : false,
44528
44529     // private
44530     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
44531     
44532     isLayout : true,
44533     
44534     // private
44535     onRender : function(ct, position){
44536         if(this.el){ // from markup
44537             this.el = Roo.get(this.el);
44538         }else {  // generate
44539             var cfg = this.getAutoCreate();
44540             this.el = ct.createChild(cfg, position);
44541         }
44542         if(this.style){
44543             this.el.applyStyles(this.style);
44544         }
44545         if(this.labelAlign){
44546             this.el.addClass('x-form-label-'+this.labelAlign);
44547         }
44548         if(this.hideLabels){
44549             this.labelStyle = "display:none";
44550             this.elementStyle = "padding-left:0;";
44551         }else{
44552             if(typeof this.labelWidth == 'number'){
44553                 this.labelStyle = "width:"+this.labelWidth+"px;";
44554                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
44555             }
44556             if(this.labelAlign == 'top'){
44557                 this.labelStyle = "width:auto;";
44558                 this.elementStyle = "padding-left:0;";
44559             }
44560         }
44561         var stack = this.stack;
44562         var slen = stack.length;
44563         if(slen > 0){
44564             if(!this.fieldTpl){
44565                 var t = new Roo.Template(
44566                     '<div class="x-form-item {5}">',
44567                         '<label for="{0}" style="{2}">{1}{4}</label>',
44568                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
44569                         '</div>',
44570                     '</div><div class="x-form-clear-left"></div>'
44571                 );
44572                 t.disableFormats = true;
44573                 t.compile();
44574                 Roo.form.Layout.prototype.fieldTpl = t;
44575             }
44576             for(var i = 0; i < slen; i++) {
44577                 if(stack[i].isFormField){
44578                     this.renderField(stack[i]);
44579                 }else{
44580                     this.renderComponent(stack[i]);
44581                 }
44582             }
44583         }
44584         if(this.clear){
44585             this.el.createChild({cls:'x-form-clear'});
44586         }
44587     },
44588
44589     // private
44590     renderField : function(f){
44591         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
44592                f.id, //0
44593                f.fieldLabel, //1
44594                f.labelStyle||this.labelStyle||'', //2
44595                this.elementStyle||'', //3
44596                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
44597                f.itemCls||this.itemCls||''  //5
44598        ], true).getPrevSibling());
44599     },
44600
44601     // private
44602     renderComponent : function(c){
44603         c.render(c.isLayout ? this.el : this.el.createChild());    
44604     },
44605     /**
44606      * Adds a object form elements (using the xtype property as the factory method.)
44607      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
44608      * @param {Object} config 
44609      */
44610     addxtype : function(o)
44611     {
44612         // create the lement.
44613         o.form = this.form;
44614         var fe = Roo.factory(o, Roo.form);
44615         this.form.allItems.push(fe);
44616         this.stack.push(fe);
44617         
44618         if (fe.isFormField) {
44619             this.form.items.add(fe);
44620         }
44621          
44622         return fe;
44623     }
44624 });
44625
44626 /**
44627  * @class Roo.form.Column
44628  * @extends Roo.form.Layout
44629  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
44630  * @constructor
44631  * @param {Object} config Configuration options
44632  */
44633 Roo.form.Column = function(config){
44634     Roo.form.Column.superclass.constructor.call(this, config);
44635 };
44636
44637 Roo.extend(Roo.form.Column, Roo.form.Layout, {
44638     /**
44639      * @cfg {Number/String} width
44640      * The fixed width of the column in pixels or CSS value (defaults to "auto")
44641      */
44642     /**
44643      * @cfg {String/Object} autoCreate
44644      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
44645      */
44646
44647     // private
44648     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
44649
44650     // private
44651     onRender : function(ct, position){
44652         Roo.form.Column.superclass.onRender.call(this, ct, position);
44653         if(this.width){
44654             this.el.setWidth(this.width);
44655         }
44656     }
44657 });
44658
44659
44660 /**
44661  * @class Roo.form.Row
44662  * @extends Roo.form.Layout
44663  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
44664  * @constructor
44665  * @param {Object} config Configuration options
44666  */
44667
44668  
44669 Roo.form.Row = function(config){
44670     Roo.form.Row.superclass.constructor.call(this, config);
44671 };
44672  
44673 Roo.extend(Roo.form.Row, Roo.form.Layout, {
44674       /**
44675      * @cfg {Number/String} width
44676      * The fixed width of the column in pixels or CSS value (defaults to "auto")
44677      */
44678     /**
44679      * @cfg {Number/String} height
44680      * The fixed height of the column in pixels or CSS value (defaults to "auto")
44681      */
44682     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
44683     
44684     padWidth : 20,
44685     // private
44686     onRender : function(ct, position){
44687         //console.log('row render');
44688         if(!this.rowTpl){
44689             var t = new Roo.Template(
44690                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
44691                     '<label for="{0}" style="{2}">{1}{4}</label>',
44692                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
44693                     '</div>',
44694                 '</div>'
44695             );
44696             t.disableFormats = true;
44697             t.compile();
44698             Roo.form.Layout.prototype.rowTpl = t;
44699         }
44700         this.fieldTpl = this.rowTpl;
44701         
44702         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
44703         var labelWidth = 100;
44704         
44705         if ((this.labelAlign != 'top')) {
44706             if (typeof this.labelWidth == 'number') {
44707                 labelWidth = this.labelWidth
44708             }
44709             this.padWidth =  20 + labelWidth;
44710             
44711         }
44712         
44713         Roo.form.Column.superclass.onRender.call(this, ct, position);
44714         if(this.width){
44715             this.el.setWidth(this.width);
44716         }
44717         if(this.height){
44718             this.el.setHeight(this.height);
44719         }
44720     },
44721     
44722     // private
44723     renderField : function(f){
44724         f.fieldEl = this.fieldTpl.append(this.el, [
44725                f.id, f.fieldLabel,
44726                f.labelStyle||this.labelStyle||'',
44727                this.elementStyle||'',
44728                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
44729                f.itemCls||this.itemCls||'',
44730                f.width ? f.width + this.padWidth : 160 + this.padWidth
44731        ],true);
44732     }
44733 });
44734  
44735
44736 /**
44737  * @class Roo.form.FieldSet
44738  * @extends Roo.form.Layout
44739  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
44740  * @constructor
44741  * @param {Object} config Configuration options
44742  */
44743 Roo.form.FieldSet = function(config){
44744     Roo.form.FieldSet.superclass.constructor.call(this, config);
44745 };
44746
44747 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
44748     /**
44749      * @cfg {String} legend
44750      * The text to display as the legend for the FieldSet (defaults to '')
44751      */
44752     /**
44753      * @cfg {String/Object} autoCreate
44754      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
44755      */
44756
44757     // private
44758     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
44759
44760     // private
44761     onRender : function(ct, position){
44762         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
44763         if(this.legend){
44764             this.setLegend(this.legend);
44765         }
44766     },
44767
44768     // private
44769     setLegend : function(text){
44770         if(this.rendered){
44771             this.el.child('legend').update(text);
44772         }
44773     }
44774 });/*
44775  * Based on:
44776  * Ext JS Library 1.1.1
44777  * Copyright(c) 2006-2007, Ext JS, LLC.
44778  *
44779  * Originally Released Under LGPL - original licence link has changed is not relivant.
44780  *
44781  * Fork - LGPL
44782  * <script type="text/javascript">
44783  */
44784 /**
44785  * @class Roo.form.VTypes
44786  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
44787  * @singleton
44788  */
44789 Roo.form.VTypes = function(){
44790     // closure these in so they are only created once.
44791     var alpha = /^[a-zA-Z_]+$/;
44792     var alphanum = /^[a-zA-Z0-9_]+$/;
44793     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
44794     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
44795
44796     // All these messages and functions are configurable
44797     return {
44798         /**
44799          * The function used to validate email addresses
44800          * @param {String} value The email address
44801          */
44802         'email' : function(v){
44803             return email.test(v);
44804         },
44805         /**
44806          * The error text to display when the email validation function returns false
44807          * @type String
44808          */
44809         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
44810         /**
44811          * The keystroke filter mask to be applied on email input
44812          * @type RegExp
44813          */
44814         'emailMask' : /[a-z0-9_\.\-@]/i,
44815
44816         /**
44817          * The function used to validate URLs
44818          * @param {String} value The URL
44819          */
44820         'url' : function(v){
44821             return url.test(v);
44822         },
44823         /**
44824          * The error text to display when the url validation function returns false
44825          * @type String
44826          */
44827         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
44828         
44829         /**
44830          * The function used to validate alpha values
44831          * @param {String} value The value
44832          */
44833         'alpha' : function(v){
44834             return alpha.test(v);
44835         },
44836         /**
44837          * The error text to display when the alpha validation function returns false
44838          * @type String
44839          */
44840         'alphaText' : 'This field should only contain letters and _',
44841         /**
44842          * The keystroke filter mask to be applied on alpha input
44843          * @type RegExp
44844          */
44845         'alphaMask' : /[a-z_]/i,
44846
44847         /**
44848          * The function used to validate alphanumeric values
44849          * @param {String} value The value
44850          */
44851         'alphanum' : function(v){
44852             return alphanum.test(v);
44853         },
44854         /**
44855          * The error text to display when the alphanumeric validation function returns false
44856          * @type String
44857          */
44858         'alphanumText' : 'This field should only contain letters, numbers and _',
44859         /**
44860          * The keystroke filter mask to be applied on alphanumeric input
44861          * @type RegExp
44862          */
44863         'alphanumMask' : /[a-z0-9_]/i
44864     };
44865 }();//<script type="text/javascript">
44866
44867 /**
44868  * @class Roo.form.FCKeditor
44869  * @extends Roo.form.TextArea
44870  * Wrapper around the FCKEditor http://www.fckeditor.net
44871  * @constructor
44872  * Creates a new FCKeditor
44873  * @param {Object} config Configuration options
44874  */
44875 Roo.form.FCKeditor = function(config){
44876     Roo.form.FCKeditor.superclass.constructor.call(this, config);
44877     this.addEvents({
44878          /**
44879          * @event editorinit
44880          * Fired when the editor is initialized - you can add extra handlers here..
44881          * @param {FCKeditor} this
44882          * @param {Object} the FCK object.
44883          */
44884         editorinit : true
44885     });
44886     
44887     
44888 };
44889 Roo.form.FCKeditor.editors = { };
44890 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
44891 {
44892     //defaultAutoCreate : {
44893     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
44894     //},
44895     // private
44896     /**
44897      * @cfg {Object} fck options - see fck manual for details.
44898      */
44899     fckconfig : false,
44900     
44901     /**
44902      * @cfg {Object} fck toolbar set (Basic or Default)
44903      */
44904     toolbarSet : 'Basic',
44905     /**
44906      * @cfg {Object} fck BasePath
44907      */ 
44908     basePath : '/fckeditor/',
44909     
44910     
44911     frame : false,
44912     
44913     value : '',
44914     
44915    
44916     onRender : function(ct, position)
44917     {
44918         if(!this.el){
44919             this.defaultAutoCreate = {
44920                 tag: "textarea",
44921                 style:"width:300px;height:60px;",
44922                 autocomplete: "off"
44923             };
44924         }
44925         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
44926         /*
44927         if(this.grow){
44928             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
44929             if(this.preventScrollbars){
44930                 this.el.setStyle("overflow", "hidden");
44931             }
44932             this.el.setHeight(this.growMin);
44933         }
44934         */
44935         //console.log('onrender' + this.getId() );
44936         Roo.form.FCKeditor.editors[this.getId()] = this;
44937          
44938
44939         this.replaceTextarea() ;
44940         
44941     },
44942     
44943     getEditor : function() {
44944         return this.fckEditor;
44945     },
44946     /**
44947      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
44948      * @param {Mixed} value The value to set
44949      */
44950     
44951     
44952     setValue : function(value)
44953     {
44954         //console.log('setValue: ' + value);
44955         
44956         if(typeof(value) == 'undefined') { // not sure why this is happending...
44957             return;
44958         }
44959         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44960         
44961         //if(!this.el || !this.getEditor()) {
44962         //    this.value = value;
44963             //this.setValue.defer(100,this,[value]);    
44964         //    return;
44965         //} 
44966         
44967         if(!this.getEditor()) {
44968             return;
44969         }
44970         
44971         this.getEditor().SetData(value);
44972         
44973         //
44974
44975     },
44976
44977     /**
44978      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
44979      * @return {Mixed} value The field value
44980      */
44981     getValue : function()
44982     {
44983         
44984         if (this.frame && this.frame.dom.style.display == 'none') {
44985             return Roo.form.FCKeditor.superclass.getValue.call(this);
44986         }
44987         
44988         if(!this.el || !this.getEditor()) {
44989            
44990            // this.getValue.defer(100,this); 
44991             return this.value;
44992         }
44993        
44994         
44995         var value=this.getEditor().GetData();
44996         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44997         return Roo.form.FCKeditor.superclass.getValue.call(this);
44998         
44999
45000     },
45001
45002     /**
45003      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
45004      * @return {Mixed} value The field value
45005      */
45006     getRawValue : function()
45007     {
45008         if (this.frame && this.frame.dom.style.display == 'none') {
45009             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45010         }
45011         
45012         if(!this.el || !this.getEditor()) {
45013             //this.getRawValue.defer(100,this); 
45014             return this.value;
45015             return;
45016         }
45017         
45018         
45019         
45020         var value=this.getEditor().GetData();
45021         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
45022         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45023          
45024     },
45025     
45026     setSize : function(w,h) {
45027         
45028         
45029         
45030         //if (this.frame && this.frame.dom.style.display == 'none') {
45031         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45032         //    return;
45033         //}
45034         //if(!this.el || !this.getEditor()) {
45035         //    this.setSize.defer(100,this, [w,h]); 
45036         //    return;
45037         //}
45038         
45039         
45040         
45041         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45042         
45043         this.frame.dom.setAttribute('width', w);
45044         this.frame.dom.setAttribute('height', h);
45045         this.frame.setSize(w,h);
45046         
45047     },
45048     
45049     toggleSourceEdit : function(value) {
45050         
45051       
45052          
45053         this.el.dom.style.display = value ? '' : 'none';
45054         this.frame.dom.style.display = value ?  'none' : '';
45055         
45056     },
45057     
45058     
45059     focus: function(tag)
45060     {
45061         if (this.frame.dom.style.display == 'none') {
45062             return Roo.form.FCKeditor.superclass.focus.call(this);
45063         }
45064         if(!this.el || !this.getEditor()) {
45065             this.focus.defer(100,this, [tag]); 
45066             return;
45067         }
45068         
45069         
45070         
45071         
45072         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
45073         this.getEditor().Focus();
45074         if (tgs.length) {
45075             if (!this.getEditor().Selection.GetSelection()) {
45076                 this.focus.defer(100,this, [tag]); 
45077                 return;
45078             }
45079             
45080             
45081             var r = this.getEditor().EditorDocument.createRange();
45082             r.setStart(tgs[0],0);
45083             r.setEnd(tgs[0],0);
45084             this.getEditor().Selection.GetSelection().removeAllRanges();
45085             this.getEditor().Selection.GetSelection().addRange(r);
45086             this.getEditor().Focus();
45087         }
45088         
45089     },
45090     
45091     
45092     
45093     replaceTextarea : function()
45094     {
45095         if ( document.getElementById( this.getId() + '___Frame' ) )
45096             return ;
45097         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
45098         //{
45099             // We must check the elements firstly using the Id and then the name.
45100         var oTextarea = document.getElementById( this.getId() );
45101         
45102         var colElementsByName = document.getElementsByName( this.getId() ) ;
45103          
45104         oTextarea.style.display = 'none' ;
45105
45106         if ( oTextarea.tabIndex ) {            
45107             this.TabIndex = oTextarea.tabIndex ;
45108         }
45109         
45110         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
45111         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
45112         this.frame = Roo.get(this.getId() + '___Frame')
45113     },
45114     
45115     _getConfigHtml : function()
45116     {
45117         var sConfig = '' ;
45118
45119         for ( var o in this.fckconfig ) {
45120             sConfig += sConfig.length > 0  ? '&amp;' : '';
45121             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
45122         }
45123
45124         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
45125     },
45126     
45127     
45128     _getIFrameHtml : function()
45129     {
45130         var sFile = 'fckeditor.html' ;
45131         /* no idea what this is about..
45132         try
45133         {
45134             if ( (/fcksource=true/i).test( window.top.location.search ) )
45135                 sFile = 'fckeditor.original.html' ;
45136         }
45137         catch (e) { 
45138         */
45139
45140         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
45141         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
45142         
45143         
45144         var html = '<iframe id="' + this.getId() +
45145             '___Frame" src="' + sLink +
45146             '" width="' + this.width +
45147             '" height="' + this.height + '"' +
45148             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
45149             ' frameborder="0" scrolling="no"></iframe>' ;
45150
45151         return html ;
45152     },
45153     
45154     _insertHtmlBefore : function( html, element )
45155     {
45156         if ( element.insertAdjacentHTML )       {
45157             // IE
45158             element.insertAdjacentHTML( 'beforeBegin', html ) ;
45159         } else { // Gecko
45160             var oRange = document.createRange() ;
45161             oRange.setStartBefore( element ) ;
45162             var oFragment = oRange.createContextualFragment( html );
45163             element.parentNode.insertBefore( oFragment, element ) ;
45164         }
45165     }
45166     
45167     
45168   
45169     
45170     
45171     
45172     
45173
45174 });
45175
45176 //Roo.reg('fckeditor', Roo.form.FCKeditor);
45177
45178 function FCKeditor_OnComplete(editorInstance){
45179     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
45180     f.fckEditor = editorInstance;
45181     //console.log("loaded");
45182     f.fireEvent('editorinit', f, editorInstance);
45183
45184   
45185
45186  
45187
45188
45189
45190
45191
45192
45193
45194
45195
45196
45197
45198
45199
45200
45201
45202 //<script type="text/javascript">
45203 /**
45204  * @class Roo.form.GridField
45205  * @extends Roo.form.Field
45206  * Embed a grid (or editable grid into a form)
45207  * STATUS ALPHA
45208  * 
45209  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
45210  * it needs 
45211  * xgrid.store = Roo.data.Store
45212  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
45213  * xgrid.store.reader = Roo.data.JsonReader 
45214  * 
45215  * 
45216  * @constructor
45217  * Creates a new GridField
45218  * @param {Object} config Configuration options
45219  */
45220 Roo.form.GridField = function(config){
45221     Roo.form.GridField.superclass.constructor.call(this, config);
45222      
45223 };
45224
45225 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
45226     /**
45227      * @cfg {Number} width  - used to restrict width of grid..
45228      */
45229     width : 100,
45230     /**
45231      * @cfg {Number} height - used to restrict height of grid..
45232      */
45233     height : 50,
45234      /**
45235      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
45236          * 
45237          *}
45238      */
45239     xgrid : false, 
45240     /**
45241      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45242      * {tag: "input", type: "checkbox", autocomplete: "off"})
45243      */
45244    // defaultAutoCreate : { tag: 'div' },
45245     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45246     /**
45247      * @cfg {String} addTitle Text to include for adding a title.
45248      */
45249     addTitle : false,
45250     //
45251     onResize : function(){
45252         Roo.form.Field.superclass.onResize.apply(this, arguments);
45253     },
45254
45255     initEvents : function(){
45256         // Roo.form.Checkbox.superclass.initEvents.call(this);
45257         // has no events...
45258        
45259     },
45260
45261
45262     getResizeEl : function(){
45263         return this.wrap;
45264     },
45265
45266     getPositionEl : function(){
45267         return this.wrap;
45268     },
45269
45270     // private
45271     onRender : function(ct, position){
45272         
45273         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
45274         var style = this.style;
45275         delete this.style;
45276         
45277         Roo.form.GridField.superclass.onRender.call(this, ct, position);
45278         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
45279         this.viewEl = this.wrap.createChild({ tag: 'div' });
45280         if (style) {
45281             this.viewEl.applyStyles(style);
45282         }
45283         if (this.width) {
45284             this.viewEl.setWidth(this.width);
45285         }
45286         if (this.height) {
45287             this.viewEl.setHeight(this.height);
45288         }
45289         //if(this.inputValue !== undefined){
45290         //this.setValue(this.value);
45291         
45292         
45293         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
45294         
45295         
45296         this.grid.render();
45297         this.grid.getDataSource().on('remove', this.refreshValue, this);
45298         this.grid.getDataSource().on('update', this.refreshValue, this);
45299         this.grid.on('afteredit', this.refreshValue, this);
45300  
45301     },
45302      
45303     
45304     /**
45305      * Sets the value of the item. 
45306      * @param {String} either an object  or a string..
45307      */
45308     setValue : function(v){
45309         //this.value = v;
45310         v = v || []; // empty set..
45311         // this does not seem smart - it really only affects memoryproxy grids..
45312         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
45313             var ds = this.grid.getDataSource();
45314             // assumes a json reader..
45315             var data = {}
45316             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
45317             ds.loadData( data);
45318         }
45319         // clear selection so it does not get stale.
45320         if (this.grid.sm) { 
45321             this.grid.sm.clearSelections();
45322         }
45323         
45324         Roo.form.GridField.superclass.setValue.call(this, v);
45325         this.refreshValue();
45326         // should load data in the grid really....
45327     },
45328     
45329     // private
45330     refreshValue: function() {
45331          var val = [];
45332         this.grid.getDataSource().each(function(r) {
45333             val.push(r.data);
45334         });
45335         this.el.dom.value = Roo.encode(val);
45336     }
45337     
45338      
45339     
45340     
45341 });/*
45342  * Based on:
45343  * Ext JS Library 1.1.1
45344  * Copyright(c) 2006-2007, Ext JS, LLC.
45345  *
45346  * Originally Released Under LGPL - original licence link has changed is not relivant.
45347  *
45348  * Fork - LGPL
45349  * <script type="text/javascript">
45350  */
45351 /**
45352  * @class Roo.form.DisplayField
45353  * @extends Roo.form.Field
45354  * A generic Field to display non-editable data.
45355  * @constructor
45356  * Creates a new Display Field item.
45357  * @param {Object} config Configuration options
45358  */
45359 Roo.form.DisplayField = function(config){
45360     Roo.form.DisplayField.superclass.constructor.call(this, config);
45361     
45362 };
45363
45364 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
45365     inputType:      'hidden',
45366     allowBlank:     true,
45367     readOnly:         true,
45368     
45369  
45370     /**
45371      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45372      */
45373     focusClass : undefined,
45374     /**
45375      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45376      */
45377     fieldClass: 'x-form-field',
45378     
45379      /**
45380      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
45381      */
45382     valueRenderer: undefined,
45383     
45384     width: 100,
45385     /**
45386      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45387      * {tag: "input", type: "checkbox", autocomplete: "off"})
45388      */
45389      
45390  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45391
45392     onResize : function(){
45393         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
45394         
45395     },
45396
45397     initEvents : function(){
45398         // Roo.form.Checkbox.superclass.initEvents.call(this);
45399         // has no events...
45400        
45401     },
45402
45403
45404     getResizeEl : function(){
45405         return this.wrap;
45406     },
45407
45408     getPositionEl : function(){
45409         return this.wrap;
45410     },
45411
45412     // private
45413     onRender : function(ct, position){
45414         
45415         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
45416         //if(this.inputValue !== undefined){
45417         this.wrap = this.el.wrap();
45418         
45419         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
45420         
45421         if (this.bodyStyle) {
45422             this.viewEl.applyStyles(this.bodyStyle);
45423         }
45424         //this.viewEl.setStyle('padding', '2px');
45425         
45426         this.setValue(this.value);
45427         
45428     },
45429 /*
45430     // private
45431     initValue : Roo.emptyFn,
45432
45433   */
45434
45435         // private
45436     onClick : function(){
45437         
45438     },
45439
45440     /**
45441      * Sets the checked state of the checkbox.
45442      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
45443      */
45444     setValue : function(v){
45445         this.value = v;
45446         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
45447         // this might be called before we have a dom element..
45448         if (!this.viewEl) {
45449             return;
45450         }
45451         this.viewEl.dom.innerHTML = html;
45452         Roo.form.DisplayField.superclass.setValue.call(this, v);
45453
45454     }
45455 });/*
45456  * 
45457  * Licence- LGPL
45458  * 
45459  */
45460
45461 /**
45462  * @class Roo.form.DayPicker
45463  * @extends Roo.form.Field
45464  * A Day picker show [M] [T] [W] ....
45465  * @constructor
45466  * Creates a new Day Picker
45467  * @param {Object} config Configuration options
45468  */
45469 Roo.form.DayPicker= function(config){
45470     Roo.form.DayPicker.superclass.constructor.call(this, config);
45471      
45472 };
45473
45474 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
45475     /**
45476      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45477      */
45478     focusClass : undefined,
45479     /**
45480      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45481      */
45482     fieldClass: "x-form-field",
45483    
45484     /**
45485      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45486      * {tag: "input", type: "checkbox", autocomplete: "off"})
45487      */
45488     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
45489     
45490    
45491     actionMode : 'viewEl', 
45492     //
45493     // private
45494  
45495     inputType : 'hidden',
45496     
45497      
45498     inputElement: false, // real input element?
45499     basedOn: false, // ????
45500     
45501     isFormField: true, // not sure where this is needed!!!!
45502
45503     onResize : function(){
45504         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
45505         if(!this.boxLabel){
45506             this.el.alignTo(this.wrap, 'c-c');
45507         }
45508     },
45509
45510     initEvents : function(){
45511         Roo.form.Checkbox.superclass.initEvents.call(this);
45512         this.el.on("click", this.onClick,  this);
45513         this.el.on("change", this.onClick,  this);
45514     },
45515
45516
45517     getResizeEl : function(){
45518         return this.wrap;
45519     },
45520
45521     getPositionEl : function(){
45522         return this.wrap;
45523     },
45524
45525     
45526     // private
45527     onRender : function(ct, position){
45528         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45529        
45530         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
45531         
45532         var r1 = '<table><tr>';
45533         var r2 = '<tr class="x-form-daypick-icons">';
45534         for (var i=0; i < 7; i++) {
45535             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
45536             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
45537         }
45538         
45539         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
45540         viewEl.select('img').on('click', this.onClick, this);
45541         this.viewEl = viewEl;   
45542         
45543         
45544         // this will not work on Chrome!!!
45545         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
45546         this.el.on('propertychange', this.setFromHidden,  this);  //ie
45547         
45548         
45549           
45550
45551     },
45552
45553     // private
45554     initValue : Roo.emptyFn,
45555
45556     /**
45557      * Returns the checked state of the checkbox.
45558      * @return {Boolean} True if checked, else false
45559      */
45560     getValue : function(){
45561         return this.el.dom.value;
45562         
45563     },
45564
45565         // private
45566     onClick : function(e){ 
45567         //this.setChecked(!this.checked);
45568         Roo.get(e.target).toggleClass('x-menu-item-checked');
45569         this.refreshValue();
45570         //if(this.el.dom.checked != this.checked){
45571         //    this.setValue(this.el.dom.checked);
45572        // }
45573     },
45574     
45575     // private
45576     refreshValue : function()
45577     {
45578         var val = '';
45579         this.viewEl.select('img',true).each(function(e,i,n)  {
45580             val += e.is(".x-menu-item-checked") ? String(n) : '';
45581         });
45582         this.setValue(val, true);
45583     },
45584
45585     /**
45586      * Sets the checked state of the checkbox.
45587      * On is always based on a string comparison between inputValue and the param.
45588      * @param {Boolean/String} value - the value to set 
45589      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
45590      */
45591     setValue : function(v,suppressEvent){
45592         if (!this.el.dom) {
45593             return;
45594         }
45595         var old = this.el.dom.value ;
45596         this.el.dom.value = v;
45597         if (suppressEvent) {
45598             return ;
45599         }
45600          
45601         // update display..
45602         this.viewEl.select('img',true).each(function(e,i,n)  {
45603             
45604             var on = e.is(".x-menu-item-checked");
45605             var newv = v.indexOf(String(n)) > -1;
45606             if (on != newv) {
45607                 e.toggleClass('x-menu-item-checked');
45608             }
45609             
45610         });
45611         
45612         
45613         this.fireEvent('change', this, v, old);
45614         
45615         
45616     },
45617    
45618     // handle setting of hidden value by some other method!!?!?
45619     setFromHidden: function()
45620     {
45621         if(!this.el){
45622             return;
45623         }
45624         //console.log("SET FROM HIDDEN");
45625         //alert('setFrom hidden');
45626         this.setValue(this.el.dom.value);
45627     },
45628     
45629     onDestroy : function()
45630     {
45631         if(this.viewEl){
45632             Roo.get(this.viewEl).remove();
45633         }
45634          
45635         Roo.form.DayPicker.superclass.onDestroy.call(this);
45636     }
45637
45638 });/*
45639  * RooJS Library 1.1.1
45640  * Copyright(c) 2008-2011  Alan Knowles
45641  *
45642  * License - LGPL
45643  */
45644  
45645
45646 /**
45647  * @class Roo.form.ComboCheck
45648  * @extends Roo.form.ComboBox
45649  * A combobox for multiple select items.
45650  *
45651  * FIXME - could do with a reset button..
45652  * 
45653  * @constructor
45654  * Create a new ComboCheck
45655  * @param {Object} config Configuration options
45656  */
45657 Roo.form.ComboCheck = function(config){
45658     Roo.form.ComboCheck.superclass.constructor.call(this, config);
45659     // should verify some data...
45660     // like
45661     // hiddenName = required..
45662     // displayField = required
45663     // valudField == required
45664     var req= [ 'hiddenName', 'displayField', 'valueField' ];
45665     var _t = this;
45666     Roo.each(req, function(e) {
45667         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
45668             throw "Roo.form.ComboCheck : missing value for: " + e;
45669         }
45670     });
45671     
45672     
45673 };
45674
45675 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
45676      
45677      
45678     editable : false,
45679      
45680     selectedClass: 'x-menu-item-checked', 
45681     
45682     // private
45683     onRender : function(ct, position){
45684         var _t = this;
45685         
45686         
45687         
45688         if(!this.tpl){
45689             var cls = 'x-combo-list';
45690
45691             
45692             this.tpl =  new Roo.Template({
45693                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
45694                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
45695                    '<span>{' + this.displayField + '}</span>' +
45696                     '</div>' 
45697                 
45698             });
45699         }
45700  
45701         
45702         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
45703         this.view.singleSelect = false;
45704         this.view.multiSelect = true;
45705         this.view.toggleSelect = true;
45706         this.pageTb.add(new Roo.Toolbar.Fill(), {
45707             
45708             text: 'Done',
45709             handler: function()
45710             {
45711                 _t.collapse();
45712             }
45713         });
45714     },
45715     
45716     onViewOver : function(e, t){
45717         // do nothing...
45718         return;
45719         
45720     },
45721     
45722     onViewClick : function(doFocus,index){
45723         return;
45724         
45725     },
45726     select: function () {
45727         //Roo.log("SELECT CALLED");
45728     },
45729      
45730     selectByValue : function(xv, scrollIntoView){
45731         var ar = this.getValueArray();
45732         var sels = [];
45733         
45734         Roo.each(ar, function(v) {
45735             if(v === undefined || v === null){
45736                 return;
45737             }
45738             var r = this.findRecord(this.valueField, v);
45739             if(r){
45740                 sels.push(this.store.indexOf(r))
45741                 
45742             }
45743         },this);
45744         this.view.select(sels);
45745         return false;
45746     },
45747     
45748     
45749     
45750     onSelect : function(record, index){
45751        // Roo.log("onselect Called");
45752        // this is only called by the clear button now..
45753         this.view.clearSelections();
45754         this.setValue('[]');
45755         if (this.value != this.valueBefore) {
45756             this.fireEvent('change', this, this.value, this.valueBefore);
45757             this.valueBefore = this.value;
45758         }
45759     },
45760     getValueArray : function()
45761     {
45762         var ar = [] ;
45763         
45764         try {
45765             //Roo.log(this.value);
45766             if (typeof(this.value) == 'undefined') {
45767                 return [];
45768             }
45769             var ar = Roo.decode(this.value);
45770             return  ar instanceof Array ? ar : []; //?? valid?
45771             
45772         } catch(e) {
45773             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
45774             return [];
45775         }
45776          
45777     },
45778     expand : function ()
45779     {
45780         
45781         Roo.form.ComboCheck.superclass.expand.call(this);
45782         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
45783         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
45784         
45785
45786     },
45787     
45788     collapse : function(){
45789         Roo.form.ComboCheck.superclass.collapse.call(this);
45790         var sl = this.view.getSelectedIndexes();
45791         var st = this.store;
45792         var nv = [];
45793         var tv = [];
45794         var r;
45795         Roo.each(sl, function(i) {
45796             r = st.getAt(i);
45797             nv.push(r.get(this.valueField));
45798         },this);
45799         this.setValue(Roo.encode(nv));
45800         if (this.value != this.valueBefore) {
45801
45802             this.fireEvent('change', this, this.value, this.valueBefore);
45803             this.valueBefore = this.value;
45804         }
45805         
45806     },
45807     
45808     setValue : function(v){
45809         // Roo.log(v);
45810         this.value = v;
45811         
45812         var vals = this.getValueArray();
45813         var tv = [];
45814         Roo.each(vals, function(k) {
45815             var r = this.findRecord(this.valueField, k);
45816             if(r){
45817                 tv.push(r.data[this.displayField]);
45818             }else if(this.valueNotFoundText !== undefined){
45819                 tv.push( this.valueNotFoundText );
45820             }
45821         },this);
45822        // Roo.log(tv);
45823         
45824         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
45825         this.hiddenField.value = v;
45826         this.value = v;
45827     }
45828     
45829 });/*
45830  * Based on:
45831  * Ext JS Library 1.1.1
45832  * Copyright(c) 2006-2007, Ext JS, LLC.
45833  *
45834  * Originally Released Under LGPL - original licence link has changed is not relivant.
45835  *
45836  * Fork - LGPL
45837  * <script type="text/javascript">
45838  */
45839  
45840 /**
45841  * @class Roo.form.Signature
45842  * @extends Roo.form.Field
45843  * Signature field.  
45844  * @constructor
45845  * 
45846  * @param {Object} config Configuration options
45847  */
45848
45849 Roo.form.Signature = function(config){
45850     Roo.form.Signature.superclass.constructor.call(this, config);
45851     
45852     this.addEvents({// not in used??
45853          /**
45854          * @event confirm
45855          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
45856              * @param {Roo.form.Signature} combo This combo box
45857              */
45858         'confirm' : true,
45859         /**
45860          * @event reset
45861          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
45862              * @param {Roo.form.ComboBox} combo This combo box
45863              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
45864              */
45865         'reset' : true
45866     });
45867 };
45868
45869 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
45870     /**
45871      * @cfg {Object} labels Label to use when rendering a form.
45872      * defaults to 
45873      * labels : { 
45874      *      clear : "Clear",
45875      *      confirm : "Confirm"
45876      *  }
45877      */
45878     labels : { 
45879         clear : "Clear",
45880         confirm : "Confirm"
45881     },
45882     /**
45883      * @cfg {Number} width The signature panel width (defaults to 300)
45884      */
45885     width: 300,
45886     /**
45887      * @cfg {Number} height The signature panel height (defaults to 100)
45888      */
45889     height : 100,
45890     /**
45891      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
45892      */
45893     allowBlank : false,
45894     
45895     //private
45896     // {Object} signPanel The signature SVG panel element (defaults to {})
45897     signPanel : {},
45898     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
45899     isMouseDown : false,
45900     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
45901     isConfirmed : false,
45902     // {String} signatureTmp SVG mapping string (defaults to empty string)
45903     signatureTmp : '',
45904     
45905     
45906     defaultAutoCreate : { // modified by initCompnoent..
45907         tag: "input",
45908         type:"hidden"
45909     },
45910
45911     // private
45912     onRender : function(ct, position){
45913         
45914         Roo.form.Signature.superclass.onRender.call(this, ct, position);
45915         
45916         this.wrap = this.el.wrap({
45917             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
45918         });
45919         
45920         this.createToolbar(this);
45921         this.signPanel = this.wrap.createChild({
45922                 tag: 'div',
45923                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
45924             }, this.el
45925         );
45926             
45927         this.svgID = Roo.id();
45928         this.svgEl = this.signPanel.createChild({
45929               xmlns : 'http://www.w3.org/2000/svg',
45930               tag : 'svg',
45931               id : this.svgID + "-svg",
45932               width: this.width,
45933               height: this.height,
45934               viewBox: '0 0 '+this.width+' '+this.height,
45935               cn : [
45936                 {
45937                     tag: "rect",
45938                     id: this.svgID + "-svg-r",
45939                     width: this.width,
45940                     height: this.height,
45941                     fill: "#ffa"
45942                 },
45943                 {
45944                     tag: "line",
45945                     id: this.svgID + "-svg-l",
45946                     x1: "0", // start
45947                     y1: (this.height*0.8), // start set the line in 80% of height
45948                     x2: this.width, // end
45949                     y2: (this.height*0.8), // end set the line in 80% of height
45950                     'stroke': "#666",
45951                     'stroke-width': "1",
45952                     'stroke-dasharray': "3",
45953                     'shape-rendering': "crispEdges",
45954                     'pointer-events': "none"
45955                 },
45956                 {
45957                     tag: "path",
45958                     id: this.svgID + "-svg-p",
45959                     'stroke': "navy",
45960                     'stroke-width': "3",
45961                     'fill': "none",
45962                     'pointer-events': 'none'
45963                 }
45964               ]
45965         });
45966         this.createSVG();
45967         this.svgBox = this.svgEl.dom.getScreenCTM();
45968     },
45969     createSVG : function(){ 
45970         var svg = this.signPanel;
45971         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
45972         var t = this;
45973
45974         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
45975         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
45976         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
45977         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
45978         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
45979         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
45980         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
45981         
45982     },
45983     isTouchEvent : function(e){
45984         return e.type.match(/^touch/);
45985     },
45986     getCoords : function (e) {
45987         var pt    = this.svgEl.dom.createSVGPoint();
45988         pt.x = e.clientX; 
45989         pt.y = e.clientY;
45990         if (this.isTouchEvent(e)) {
45991             pt.x =  e.targetTouches[0].clientX 
45992             pt.y = e.targetTouches[0].clientY;
45993         }
45994         var a = this.svgEl.dom.getScreenCTM();
45995         var b = a.inverse();
45996         var mx = pt.matrixTransform(b);
45997         return mx.x + ',' + mx.y;
45998     },
45999     //mouse event headler 
46000     down : function (e) {
46001         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
46002         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
46003         
46004         this.isMouseDown = true;
46005         
46006         e.preventDefault();
46007     },
46008     move : function (e) {
46009         if (this.isMouseDown) {
46010             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
46011             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
46012         }
46013         
46014         e.preventDefault();
46015     },
46016     up : function (e) {
46017         this.isMouseDown = false;
46018         var sp = this.signatureTmp.split(' ');
46019         
46020         if(sp.length > 1){
46021             if(!sp[sp.length-2].match(/^L/)){
46022                 sp.pop();
46023                 sp.pop();
46024                 sp.push("");
46025                 this.signatureTmp = sp.join(" ");
46026             }
46027         }
46028         if(this.getValue() != this.signatureTmp){
46029             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46030             this.isConfirmed = false;
46031         }
46032         e.preventDefault();
46033     },
46034     
46035     /**
46036      * Protected method that will not generally be called directly. It
46037      * is called when the editor creates its toolbar. Override this method if you need to
46038      * add custom toolbar buttons.
46039      * @param {HtmlEditor} editor
46040      */
46041     createToolbar : function(editor){
46042          function btn(id, toggle, handler){
46043             var xid = fid + '-'+ id ;
46044             return {
46045                 id : xid,
46046                 cmd : id,
46047                 cls : 'x-btn-icon x-edit-'+id,
46048                 enableToggle:toggle !== false,
46049                 scope: editor, // was editor...
46050                 handler:handler||editor.relayBtnCmd,
46051                 clickEvent:'mousedown',
46052                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46053                 tabIndex:-1
46054             };
46055         }
46056         
46057         
46058         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46059         this.tb = tb;
46060         this.tb.add(
46061            {
46062                 cls : ' x-signature-btn x-signature-'+id,
46063                 scope: editor, // was editor...
46064                 handler: this.reset,
46065                 clickEvent:'mousedown',
46066                 text: this.labels.clear
46067             },
46068             {
46069                  xtype : 'Fill',
46070                  xns: Roo.Toolbar
46071             }, 
46072             {
46073                 cls : '  x-signature-btn x-signature-'+id,
46074                 scope: editor, // was editor...
46075                 handler: this.confirmHandler,
46076                 clickEvent:'mousedown',
46077                 text: this.labels.confirm
46078             }
46079         );
46080     
46081     },
46082     //public
46083     /**
46084      * when user is clicked confirm then show this image.....
46085      * 
46086      * @return {String} Image Data URI
46087      */
46088     getImageDataURI : function(){
46089         var svg = this.svgEl.dom.parentNode.innerHTML;
46090         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
46091         return src; 
46092     },
46093     /**
46094      * 
46095      * @return {Boolean} this.isConfirmed
46096      */
46097     getConfirmed : function(){
46098         return this.isConfirmed;
46099     },
46100     /**
46101      * 
46102      * @return {Number} this.width
46103      */
46104     getWidth : function(){
46105         return this.width;
46106     },
46107     /**
46108      * 
46109      * @return {Number} this.height
46110      */
46111     getHeight : function(){
46112         return this.height;
46113     },
46114     // private
46115     getSignature : function(){
46116         return this.signatureTmp;
46117     },
46118     // private
46119     reset : function(){
46120         this.signatureTmp = '';
46121         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46122         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
46123         this.isConfirmed = false;
46124         Roo.form.Signature.superclass.reset.call(this);
46125     },
46126     setSignature : function(s){
46127         this.signatureTmp = s;
46128         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46129         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
46130         this.setValue(s);
46131         this.isConfirmed = false;
46132         Roo.form.Signature.superclass.reset.call(this);
46133     }, 
46134     test : function(){
46135 //        Roo.log(this.signPanel.dom.contentWindow.up())
46136     },
46137     //private
46138     setConfirmed : function(){
46139         
46140         
46141         
46142 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
46143     },
46144     // private
46145     confirmHandler : function(){
46146         if(!this.getSignature()){
46147             return;
46148         }
46149         
46150         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
46151         this.setValue(this.getSignature());
46152         this.isConfirmed = true;
46153         
46154         this.fireEvent('confirm', this);
46155     },
46156     // private
46157     // Subclasses should provide the validation implementation by overriding this
46158     validateValue : function(value){
46159         if(this.allowBlank){
46160             return true;
46161         }
46162         
46163         if(this.isConfirmed){
46164             return true;
46165         }
46166         return false;
46167     }
46168 });//<script type="text/javasscript">
46169  
46170
46171 /**
46172  * @class Roo.DDView
46173  * A DnD enabled version of Roo.View.
46174  * @param {Element/String} container The Element in which to create the View.
46175  * @param {String} tpl The template string used to create the markup for each element of the View
46176  * @param {Object} config The configuration properties. These include all the config options of
46177  * {@link Roo.View} plus some specific to this class.<br>
46178  * <p>
46179  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
46180  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
46181  * <p>
46182  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
46183 .x-view-drag-insert-above {
46184         border-top:1px dotted #3366cc;
46185 }
46186 .x-view-drag-insert-below {
46187         border-bottom:1px dotted #3366cc;
46188 }
46189 </code></pre>
46190  * 
46191  */
46192  
46193 Roo.DDView = function(container, tpl, config) {
46194     Roo.DDView.superclass.constructor.apply(this, arguments);
46195     this.getEl().setStyle("outline", "0px none");
46196     this.getEl().unselectable();
46197     if (this.dragGroup) {
46198                 this.setDraggable(this.dragGroup.split(","));
46199     }
46200     if (this.dropGroup) {
46201                 this.setDroppable(this.dropGroup.split(","));
46202     }
46203     if (this.deletable) {
46204         this.setDeletable();
46205     }
46206     this.isDirtyFlag = false;
46207         this.addEvents({
46208                 "drop" : true
46209         });
46210 };
46211
46212 Roo.extend(Roo.DDView, Roo.View, {
46213 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
46214 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
46215 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
46216 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
46217
46218         isFormField: true,
46219
46220         reset: Roo.emptyFn,
46221         
46222         clearInvalid: Roo.form.Field.prototype.clearInvalid,
46223
46224         validate: function() {
46225                 return true;
46226         },
46227         
46228         destroy: function() {
46229                 this.purgeListeners();
46230                 this.getEl.removeAllListeners();
46231                 this.getEl().remove();
46232                 if (this.dragZone) {
46233                         if (this.dragZone.destroy) {
46234                                 this.dragZone.destroy();
46235                         }
46236                 }
46237                 if (this.dropZone) {
46238                         if (this.dropZone.destroy) {
46239                                 this.dropZone.destroy();
46240                         }
46241                 }
46242         },
46243
46244 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
46245         getName: function() {
46246                 return this.name;
46247         },
46248
46249 /**     Loads the View from a JSON string representing the Records to put into the Store. */
46250         setValue: function(v) {
46251                 if (!this.store) {
46252                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
46253                 }
46254                 var data = {};
46255                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
46256                 this.store.proxy = new Roo.data.MemoryProxy(data);
46257                 this.store.load();
46258         },
46259
46260 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
46261         getValue: function() {
46262                 var result = '(';
46263                 this.store.each(function(rec) {
46264                         result += rec.id + ',';
46265                 });
46266                 return result.substr(0, result.length - 1) + ')';
46267         },
46268         
46269         getIds: function() {
46270                 var i = 0, result = new Array(this.store.getCount());
46271                 this.store.each(function(rec) {
46272                         result[i++] = rec.id;
46273                 });
46274                 return result;
46275         },
46276         
46277         isDirty: function() {
46278                 return this.isDirtyFlag;
46279         },
46280
46281 /**
46282  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
46283  *      whole Element becomes the target, and this causes the drop gesture to append.
46284  */
46285     getTargetFromEvent : function(e) {
46286                 var target = e.getTarget();
46287                 while ((target !== null) && (target.parentNode != this.el.dom)) {
46288                 target = target.parentNode;
46289                 }
46290                 if (!target) {
46291                         target = this.el.dom.lastChild || this.el.dom;
46292                 }
46293                 return target;
46294     },
46295
46296 /**
46297  *      Create the drag data which consists of an object which has the property "ddel" as
46298  *      the drag proxy element. 
46299  */
46300     getDragData : function(e) {
46301         var target = this.findItemFromChild(e.getTarget());
46302                 if(target) {
46303                         this.handleSelection(e);
46304                         var selNodes = this.getSelectedNodes();
46305             var dragData = {
46306                 source: this,
46307                 copy: this.copy || (this.allowCopy && e.ctrlKey),
46308                 nodes: selNodes,
46309                 records: []
46310                         };
46311                         var selectedIndices = this.getSelectedIndexes();
46312                         for (var i = 0; i < selectedIndices.length; i++) {
46313                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
46314                         }
46315                         if (selNodes.length == 1) {
46316                                 dragData.ddel = target.cloneNode(true); // the div element
46317                         } else {
46318                                 var div = document.createElement('div'); // create the multi element drag "ghost"
46319                                 div.className = 'multi-proxy';
46320                                 for (var i = 0, len = selNodes.length; i < len; i++) {
46321                                         div.appendChild(selNodes[i].cloneNode(true));
46322                                 }
46323                                 dragData.ddel = div;
46324                         }
46325             //console.log(dragData)
46326             //console.log(dragData.ddel.innerHTML)
46327                         return dragData;
46328                 }
46329         //console.log('nodragData')
46330                 return false;
46331     },
46332     
46333 /**     Specify to which ddGroup items in this DDView may be dragged. */
46334     setDraggable: function(ddGroup) {
46335         if (ddGroup instanceof Array) {
46336                 Roo.each(ddGroup, this.setDraggable, this);
46337                 return;
46338         }
46339         if (this.dragZone) {
46340                 this.dragZone.addToGroup(ddGroup);
46341         } else {
46342                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
46343                                 containerScroll: true,
46344                                 ddGroup: ddGroup 
46345
46346                         });
46347 //                      Draggability implies selection. DragZone's mousedown selects the element.
46348                         if (!this.multiSelect) { this.singleSelect = true; }
46349
46350 //                      Wire the DragZone's handlers up to methods in *this*
46351                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
46352                 }
46353     },
46354
46355 /**     Specify from which ddGroup this DDView accepts drops. */
46356     setDroppable: function(ddGroup) {
46357         if (ddGroup instanceof Array) {
46358                 Roo.each(ddGroup, this.setDroppable, this);
46359                 return;
46360         }
46361         if (this.dropZone) {
46362                 this.dropZone.addToGroup(ddGroup);
46363         } else {
46364                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
46365                                 containerScroll: true,
46366                                 ddGroup: ddGroup
46367                         });
46368
46369 //                      Wire the DropZone's handlers up to methods in *this*
46370                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
46371                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
46372                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
46373                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
46374                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
46375                 }
46376     },
46377
46378 /**     Decide whether to drop above or below a View node. */
46379     getDropPoint : function(e, n, dd){
46380         if (n == this.el.dom) { return "above"; }
46381                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
46382                 var c = t + (b - t) / 2;
46383                 var y = Roo.lib.Event.getPageY(e);
46384                 if(y <= c) {
46385                         return "above";
46386                 }else{
46387                         return "below";
46388                 }
46389     },
46390
46391     onNodeEnter : function(n, dd, e, data){
46392                 return false;
46393     },
46394     
46395     onNodeOver : function(n, dd, e, data){
46396                 var pt = this.getDropPoint(e, n, dd);
46397                 // set the insert point style on the target node
46398                 var dragElClass = this.dropNotAllowed;
46399                 if (pt) {
46400                         var targetElClass;
46401                         if (pt == "above"){
46402                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
46403                                 targetElClass = "x-view-drag-insert-above";
46404                         } else {
46405                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
46406                                 targetElClass = "x-view-drag-insert-below";
46407                         }
46408                         if (this.lastInsertClass != targetElClass){
46409                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
46410                                 this.lastInsertClass = targetElClass;
46411                         }
46412                 }
46413                 return dragElClass;
46414         },
46415
46416     onNodeOut : function(n, dd, e, data){
46417                 this.removeDropIndicators(n);
46418     },
46419
46420     onNodeDrop : function(n, dd, e, data){
46421         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
46422                 return false;
46423         }
46424         var pt = this.getDropPoint(e, n, dd);
46425                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
46426                 if (pt == "below") { insertAt++; }
46427                 for (var i = 0; i < data.records.length; i++) {
46428                         var r = data.records[i];
46429                         var dup = this.store.getById(r.id);
46430                         if (dup && (dd != this.dragZone)) {
46431                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
46432                         } else {
46433                                 if (data.copy) {
46434                                         this.store.insert(insertAt++, r.copy());
46435                                 } else {
46436                                         data.source.isDirtyFlag = true;
46437                                         r.store.remove(r);
46438                                         this.store.insert(insertAt++, r);
46439                                 }
46440                                 this.isDirtyFlag = true;
46441                         }
46442                 }
46443                 this.dragZone.cachedTarget = null;
46444                 return true;
46445     },
46446
46447     removeDropIndicators : function(n){
46448                 if(n){
46449                         Roo.fly(n).removeClass([
46450                                 "x-view-drag-insert-above",
46451                                 "x-view-drag-insert-below"]);
46452                         this.lastInsertClass = "_noclass";
46453                 }
46454     },
46455
46456 /**
46457  *      Utility method. Add a delete option to the DDView's context menu.
46458  *      @param {String} imageUrl The URL of the "delete" icon image.
46459  */
46460         setDeletable: function(imageUrl) {
46461                 if (!this.singleSelect && !this.multiSelect) {
46462                         this.singleSelect = true;
46463                 }
46464                 var c = this.getContextMenu();
46465                 this.contextMenu.on("itemclick", function(item) {
46466                         switch (item.id) {
46467                                 case "delete":
46468                                         this.remove(this.getSelectedIndexes());
46469                                         break;
46470                         }
46471                 }, this);
46472                 this.contextMenu.add({
46473                         icon: imageUrl,
46474                         id: "delete",
46475                         text: 'Delete'
46476                 });
46477         },
46478         
46479 /**     Return the context menu for this DDView. */
46480         getContextMenu: function() {
46481                 if (!this.contextMenu) {
46482 //                      Create the View's context menu
46483                         this.contextMenu = new Roo.menu.Menu({
46484                                 id: this.id + "-contextmenu"
46485                         });
46486                         this.el.on("contextmenu", this.showContextMenu, this);
46487                 }
46488                 return this.contextMenu;
46489         },
46490         
46491         disableContextMenu: function() {
46492                 if (this.contextMenu) {
46493                         this.el.un("contextmenu", this.showContextMenu, this);
46494                 }
46495         },
46496
46497         showContextMenu: function(e, item) {
46498         item = this.findItemFromChild(e.getTarget());
46499                 if (item) {
46500                         e.stopEvent();
46501                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
46502                         this.contextMenu.showAt(e.getXY());
46503             }
46504     },
46505
46506 /**
46507  *      Remove {@link Roo.data.Record}s at the specified indices.
46508  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
46509  */
46510     remove: function(selectedIndices) {
46511                 selectedIndices = [].concat(selectedIndices);
46512                 for (var i = 0; i < selectedIndices.length; i++) {
46513                         var rec = this.store.getAt(selectedIndices[i]);
46514                         this.store.remove(rec);
46515                 }
46516     },
46517
46518 /**
46519  *      Double click fires the event, but also, if this is draggable, and there is only one other
46520  *      related DropZone, it transfers the selected node.
46521  */
46522     onDblClick : function(e){
46523         var item = this.findItemFromChild(e.getTarget());
46524         if(item){
46525             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
46526                 return false;
46527             }
46528             if (this.dragGroup) {
46529                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
46530                     while (targets.indexOf(this.dropZone) > -1) {
46531                             targets.remove(this.dropZone);
46532                                 }
46533                     if (targets.length == 1) {
46534                                         this.dragZone.cachedTarget = null;
46535                         var el = Roo.get(targets[0].getEl());
46536                         var box = el.getBox(true);
46537                         targets[0].onNodeDrop(el.dom, {
46538                                 target: el.dom,
46539                                 xy: [box.x, box.y + box.height - 1]
46540                         }, null, this.getDragData(e));
46541                     }
46542                 }
46543         }
46544     },
46545     
46546     handleSelection: function(e) {
46547                 this.dragZone.cachedTarget = null;
46548         var item = this.findItemFromChild(e.getTarget());
46549         if (!item) {
46550                 this.clearSelections(true);
46551                 return;
46552         }
46553                 if (item && (this.multiSelect || this.singleSelect)){
46554                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
46555                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
46556                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
46557                                 this.unselect(item);
46558                         } else {
46559                                 this.select(item, this.multiSelect && e.ctrlKey);
46560                                 this.lastSelection = item;
46561                         }
46562                 }
46563     },
46564
46565     onItemClick : function(item, index, e){
46566                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
46567                         return false;
46568                 }
46569                 return true;
46570     },
46571
46572     unselect : function(nodeInfo, suppressEvent){
46573                 var node = this.getNode(nodeInfo);
46574                 if(node && this.isSelected(node)){
46575                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
46576                                 Roo.fly(node).removeClass(this.selectedClass);
46577                                 this.selections.remove(node);
46578                                 if(!suppressEvent){
46579                                         this.fireEvent("selectionchange", this, this.selections);
46580                                 }
46581                         }
46582                 }
46583     }
46584 });
46585 /*
46586  * Based on:
46587  * Ext JS Library 1.1.1
46588  * Copyright(c) 2006-2007, Ext JS, LLC.
46589  *
46590  * Originally Released Under LGPL - original licence link has changed is not relivant.
46591  *
46592  * Fork - LGPL
46593  * <script type="text/javascript">
46594  */
46595  
46596 /**
46597  * @class Roo.LayoutManager
46598  * @extends Roo.util.Observable
46599  * Base class for layout managers.
46600  */
46601 Roo.LayoutManager = function(container, config){
46602     Roo.LayoutManager.superclass.constructor.call(this);
46603     this.el = Roo.get(container);
46604     // ie scrollbar fix
46605     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
46606         document.body.scroll = "no";
46607     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
46608         this.el.position('relative');
46609     }
46610     this.id = this.el.id;
46611     this.el.addClass("x-layout-container");
46612     /** false to disable window resize monitoring @type Boolean */
46613     this.monitorWindowResize = true;
46614     this.regions = {};
46615     this.addEvents({
46616         /**
46617          * @event layout
46618          * Fires when a layout is performed. 
46619          * @param {Roo.LayoutManager} this
46620          */
46621         "layout" : true,
46622         /**
46623          * @event regionresized
46624          * Fires when the user resizes a region. 
46625          * @param {Roo.LayoutRegion} region The resized region
46626          * @param {Number} newSize The new size (width for east/west, height for north/south)
46627          */
46628         "regionresized" : true,
46629         /**
46630          * @event regioncollapsed
46631          * Fires when a region is collapsed. 
46632          * @param {Roo.LayoutRegion} region The collapsed region
46633          */
46634         "regioncollapsed" : true,
46635         /**
46636          * @event regionexpanded
46637          * Fires when a region is expanded.  
46638          * @param {Roo.LayoutRegion} region The expanded region
46639          */
46640         "regionexpanded" : true
46641     });
46642     this.updating = false;
46643     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
46644 };
46645
46646 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
46647     /**
46648      * Returns true if this layout is currently being updated
46649      * @return {Boolean}
46650      */
46651     isUpdating : function(){
46652         return this.updating; 
46653     },
46654     
46655     /**
46656      * Suspend the LayoutManager from doing auto-layouts while
46657      * making multiple add or remove calls
46658      */
46659     beginUpdate : function(){
46660         this.updating = true;    
46661     },
46662     
46663     /**
46664      * Restore auto-layouts and optionally disable the manager from performing a layout
46665      * @param {Boolean} noLayout true to disable a layout update 
46666      */
46667     endUpdate : function(noLayout){
46668         this.updating = false;
46669         if(!noLayout){
46670             this.layout();
46671         }    
46672     },
46673     
46674     layout: function(){
46675         
46676     },
46677     
46678     onRegionResized : function(region, newSize){
46679         this.fireEvent("regionresized", region, newSize);
46680         this.layout();
46681     },
46682     
46683     onRegionCollapsed : function(region){
46684         this.fireEvent("regioncollapsed", region);
46685     },
46686     
46687     onRegionExpanded : function(region){
46688         this.fireEvent("regionexpanded", region);
46689     },
46690         
46691     /**
46692      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
46693      * performs box-model adjustments.
46694      * @return {Object} The size as an object {width: (the width), height: (the height)}
46695      */
46696     getViewSize : function(){
46697         var size;
46698         if(this.el.dom != document.body){
46699             size = this.el.getSize();
46700         }else{
46701             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
46702         }
46703         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
46704         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46705         return size;
46706     },
46707     
46708     /**
46709      * Returns the Element this layout is bound to.
46710      * @return {Roo.Element}
46711      */
46712     getEl : function(){
46713         return this.el;
46714     },
46715     
46716     /**
46717      * Returns the specified region.
46718      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
46719      * @return {Roo.LayoutRegion}
46720      */
46721     getRegion : function(target){
46722         return this.regions[target.toLowerCase()];
46723     },
46724     
46725     onWindowResize : function(){
46726         if(this.monitorWindowResize){
46727             this.layout();
46728         }
46729     }
46730 });/*
46731  * Based on:
46732  * Ext JS Library 1.1.1
46733  * Copyright(c) 2006-2007, Ext JS, LLC.
46734  *
46735  * Originally Released Under LGPL - original licence link has changed is not relivant.
46736  *
46737  * Fork - LGPL
46738  * <script type="text/javascript">
46739  */
46740 /**
46741  * @class Roo.BorderLayout
46742  * @extends Roo.LayoutManager
46743  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
46744  * please see: <br><br>
46745  * <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>
46746  * <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>
46747  * Example:
46748  <pre><code>
46749  var layout = new Roo.BorderLayout(document.body, {
46750     north: {
46751         initialSize: 25,
46752         titlebar: false
46753     },
46754     west: {
46755         split:true,
46756         initialSize: 200,
46757         minSize: 175,
46758         maxSize: 400,
46759         titlebar: true,
46760         collapsible: true
46761     },
46762     east: {
46763         split:true,
46764         initialSize: 202,
46765         minSize: 175,
46766         maxSize: 400,
46767         titlebar: true,
46768         collapsible: true
46769     },
46770     south: {
46771         split:true,
46772         initialSize: 100,
46773         minSize: 100,
46774         maxSize: 200,
46775         titlebar: true,
46776         collapsible: true
46777     },
46778     center: {
46779         titlebar: true,
46780         autoScroll:true,
46781         resizeTabs: true,
46782         minTabWidth: 50,
46783         preferredTabWidth: 150
46784     }
46785 });
46786
46787 // shorthand
46788 var CP = Roo.ContentPanel;
46789
46790 layout.beginUpdate();
46791 layout.add("north", new CP("north", "North"));
46792 layout.add("south", new CP("south", {title: "South", closable: true}));
46793 layout.add("west", new CP("west", {title: "West"}));
46794 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
46795 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
46796 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
46797 layout.getRegion("center").showPanel("center1");
46798 layout.endUpdate();
46799 </code></pre>
46800
46801 <b>The container the layout is rendered into can be either the body element or any other element.
46802 If it is not the body element, the container needs to either be an absolute positioned element,
46803 or you will need to add "position:relative" to the css of the container.  You will also need to specify
46804 the container size if it is not the body element.</b>
46805
46806 * @constructor
46807 * Create a new BorderLayout
46808 * @param {String/HTMLElement/Element} container The container this layout is bound to
46809 * @param {Object} config Configuration options
46810  */
46811 Roo.BorderLayout = function(container, config){
46812     config = config || {};
46813     Roo.BorderLayout.superclass.constructor.call(this, container, config);
46814     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
46815     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
46816         var target = this.factory.validRegions[i];
46817         if(config[target]){
46818             this.addRegion(target, config[target]);
46819         }
46820     }
46821 };
46822
46823 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
46824     /**
46825      * Creates and adds a new region if it doesn't already exist.
46826      * @param {String} target The target region key (north, south, east, west or center).
46827      * @param {Object} config The regions config object
46828      * @return {BorderLayoutRegion} The new region
46829      */
46830     addRegion : function(target, config){
46831         if(!this.regions[target]){
46832             var r = this.factory.create(target, this, config);
46833             this.bindRegion(target, r);
46834         }
46835         return this.regions[target];
46836     },
46837
46838     // private (kinda)
46839     bindRegion : function(name, r){
46840         this.regions[name] = r;
46841         r.on("visibilitychange", this.layout, this);
46842         r.on("paneladded", this.layout, this);
46843         r.on("panelremoved", this.layout, this);
46844         r.on("invalidated", this.layout, this);
46845         r.on("resized", this.onRegionResized, this);
46846         r.on("collapsed", this.onRegionCollapsed, this);
46847         r.on("expanded", this.onRegionExpanded, this);
46848     },
46849
46850     /**
46851      * Performs a layout update.
46852      */
46853     layout : function(){
46854         if(this.updating) return;
46855         var size = this.getViewSize();
46856         var w = size.width;
46857         var h = size.height;
46858         var centerW = w;
46859         var centerH = h;
46860         var centerY = 0;
46861         var centerX = 0;
46862         //var x = 0, y = 0;
46863
46864         var rs = this.regions;
46865         var north = rs["north"];
46866         var south = rs["south"]; 
46867         var west = rs["west"];
46868         var east = rs["east"];
46869         var center = rs["center"];
46870         //if(this.hideOnLayout){ // not supported anymore
46871             //c.el.setStyle("display", "none");
46872         //}
46873         if(north && north.isVisible()){
46874             var b = north.getBox();
46875             var m = north.getMargins();
46876             b.width = w - (m.left+m.right);
46877             b.x = m.left;
46878             b.y = m.top;
46879             centerY = b.height + b.y + m.bottom;
46880             centerH -= centerY;
46881             north.updateBox(this.safeBox(b));
46882         }
46883         if(south && south.isVisible()){
46884             var b = south.getBox();
46885             var m = south.getMargins();
46886             b.width = w - (m.left+m.right);
46887             b.x = m.left;
46888             var totalHeight = (b.height + m.top + m.bottom);
46889             b.y = h - totalHeight + m.top;
46890             centerH -= totalHeight;
46891             south.updateBox(this.safeBox(b));
46892         }
46893         if(west && west.isVisible()){
46894             var b = west.getBox();
46895             var m = west.getMargins();
46896             b.height = centerH - (m.top+m.bottom);
46897             b.x = m.left;
46898             b.y = centerY + m.top;
46899             var totalWidth = (b.width + m.left + m.right);
46900             centerX += totalWidth;
46901             centerW -= totalWidth;
46902             west.updateBox(this.safeBox(b));
46903         }
46904         if(east && east.isVisible()){
46905             var b = east.getBox();
46906             var m = east.getMargins();
46907             b.height = centerH - (m.top+m.bottom);
46908             var totalWidth = (b.width + m.left + m.right);
46909             b.x = w - totalWidth + m.left;
46910             b.y = centerY + m.top;
46911             centerW -= totalWidth;
46912             east.updateBox(this.safeBox(b));
46913         }
46914         if(center){
46915             var m = center.getMargins();
46916             var centerBox = {
46917                 x: centerX + m.left,
46918                 y: centerY + m.top,
46919                 width: centerW - (m.left+m.right),
46920                 height: centerH - (m.top+m.bottom)
46921             };
46922             //if(this.hideOnLayout){
46923                 //center.el.setStyle("display", "block");
46924             //}
46925             center.updateBox(this.safeBox(centerBox));
46926         }
46927         this.el.repaint();
46928         this.fireEvent("layout", this);
46929     },
46930
46931     // private
46932     safeBox : function(box){
46933         box.width = Math.max(0, box.width);
46934         box.height = Math.max(0, box.height);
46935         return box;
46936     },
46937
46938     /**
46939      * Adds a ContentPanel (or subclass) to this layout.
46940      * @param {String} target The target region key (north, south, east, west or center).
46941      * @param {Roo.ContentPanel} panel The panel to add
46942      * @return {Roo.ContentPanel} The added panel
46943      */
46944     add : function(target, panel){
46945          
46946         target = target.toLowerCase();
46947         return this.regions[target].add(panel);
46948     },
46949
46950     /**
46951      * Remove a ContentPanel (or subclass) to this layout.
46952      * @param {String} target The target region key (north, south, east, west or center).
46953      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
46954      * @return {Roo.ContentPanel} The removed panel
46955      */
46956     remove : function(target, panel){
46957         target = target.toLowerCase();
46958         return this.regions[target].remove(panel);
46959     },
46960
46961     /**
46962      * Searches all regions for a panel with the specified id
46963      * @param {String} panelId
46964      * @return {Roo.ContentPanel} The panel or null if it wasn't found
46965      */
46966     findPanel : function(panelId){
46967         var rs = this.regions;
46968         for(var target in rs){
46969             if(typeof rs[target] != "function"){
46970                 var p = rs[target].getPanel(panelId);
46971                 if(p){
46972                     return p;
46973                 }
46974             }
46975         }
46976         return null;
46977     },
46978
46979     /**
46980      * Searches all regions for a panel with the specified id and activates (shows) it.
46981      * @param {String/ContentPanel} panelId The panels id or the panel itself
46982      * @return {Roo.ContentPanel} The shown panel or null
46983      */
46984     showPanel : function(panelId) {
46985       var rs = this.regions;
46986       for(var target in rs){
46987          var r = rs[target];
46988          if(typeof r != "function"){
46989             if(r.hasPanel(panelId)){
46990                return r.showPanel(panelId);
46991             }
46992          }
46993       }
46994       return null;
46995    },
46996
46997    /**
46998      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
46999      * @param {Roo.state.Provider} provider (optional) An alternate state provider
47000      */
47001     restoreState : function(provider){
47002         if(!provider){
47003             provider = Roo.state.Manager;
47004         }
47005         var sm = new Roo.LayoutStateManager();
47006         sm.init(this, provider);
47007     },
47008
47009     /**
47010      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
47011      * object should contain properties for each region to add ContentPanels to, and each property's value should be
47012      * a valid ContentPanel config object.  Example:
47013      * <pre><code>
47014 // Create the main layout
47015 var layout = new Roo.BorderLayout('main-ct', {
47016     west: {
47017         split:true,
47018         minSize: 175,
47019         titlebar: true
47020     },
47021     center: {
47022         title:'Components'
47023     }
47024 }, 'main-ct');
47025
47026 // Create and add multiple ContentPanels at once via configs
47027 layout.batchAdd({
47028    west: {
47029        id: 'source-files',
47030        autoCreate:true,
47031        title:'Ext Source Files',
47032        autoScroll:true,
47033        fitToFrame:true
47034    },
47035    center : {
47036        el: cview,
47037        autoScroll:true,
47038        fitToFrame:true,
47039        toolbar: tb,
47040        resizeEl:'cbody'
47041    }
47042 });
47043 </code></pre>
47044      * @param {Object} regions An object containing ContentPanel configs by region name
47045      */
47046     batchAdd : function(regions){
47047         this.beginUpdate();
47048         for(var rname in regions){
47049             var lr = this.regions[rname];
47050             if(lr){
47051                 this.addTypedPanels(lr, regions[rname]);
47052             }
47053         }
47054         this.endUpdate();
47055     },
47056
47057     // private
47058     addTypedPanels : function(lr, ps){
47059         if(typeof ps == 'string'){
47060             lr.add(new Roo.ContentPanel(ps));
47061         }
47062         else if(ps instanceof Array){
47063             for(var i =0, len = ps.length; i < len; i++){
47064                 this.addTypedPanels(lr, ps[i]);
47065             }
47066         }
47067         else if(!ps.events){ // raw config?
47068             var el = ps.el;
47069             delete ps.el; // prevent conflict
47070             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
47071         }
47072         else {  // panel object assumed!
47073             lr.add(ps);
47074         }
47075     },
47076     /**
47077      * Adds a xtype elements to the layout.
47078      * <pre><code>
47079
47080 layout.addxtype({
47081        xtype : 'ContentPanel',
47082        region: 'west',
47083        items: [ .... ]
47084    }
47085 );
47086
47087 layout.addxtype({
47088         xtype : 'NestedLayoutPanel',
47089         region: 'west',
47090         layout: {
47091            center: { },
47092            west: { }   
47093         },
47094         items : [ ... list of content panels or nested layout panels.. ]
47095    }
47096 );
47097 </code></pre>
47098      * @param {Object} cfg Xtype definition of item to add.
47099      */
47100     addxtype : function(cfg)
47101     {
47102         // basically accepts a pannel...
47103         // can accept a layout region..!?!?
47104         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
47105         
47106         if (!cfg.xtype.match(/Panel$/)) {
47107             return false;
47108         }
47109         var ret = false;
47110         
47111         if (typeof(cfg.region) == 'undefined') {
47112             Roo.log("Failed to add Panel, region was not set");
47113             Roo.log(cfg);
47114             return false;
47115         }
47116         var region = cfg.region;
47117         delete cfg.region;
47118         
47119           
47120         var xitems = [];
47121         if (cfg.items) {
47122             xitems = cfg.items;
47123             delete cfg.items;
47124         }
47125         var nb = false;
47126         
47127         switch(cfg.xtype) 
47128         {
47129             case 'ContentPanel':  // ContentPanel (el, cfg)
47130             case 'ScrollPanel':  // ContentPanel (el, cfg)
47131             case 'ViewPanel': 
47132                 if(cfg.autoCreate) {
47133                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
47134                 } else {
47135                     var el = this.el.createChild();
47136                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
47137                 }
47138                 
47139                 this.add(region, ret);
47140                 break;
47141             
47142             
47143             case 'TreePanel': // our new panel!
47144                 cfg.el = this.el.createChild();
47145                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
47146                 this.add(region, ret);
47147                 break;
47148             
47149             case 'NestedLayoutPanel': 
47150                 // create a new Layout (which is  a Border Layout...
47151                 var el = this.el.createChild();
47152                 var clayout = cfg.layout;
47153                 delete cfg.layout;
47154                 clayout.items   = clayout.items  || [];
47155                 // replace this exitems with the clayout ones..
47156                 xitems = clayout.items;
47157                  
47158                 
47159                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
47160                     cfg.background = false;
47161                 }
47162                 var layout = new Roo.BorderLayout(el, clayout);
47163                 
47164                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
47165                 //console.log('adding nested layout panel '  + cfg.toSource());
47166                 this.add(region, ret);
47167                 nb = {}; /// find first...
47168                 break;
47169                 
47170             case 'GridPanel': 
47171             
47172                 // needs grid and region
47173                 
47174                 //var el = this.getRegion(region).el.createChild();
47175                 var el = this.el.createChild();
47176                 // create the grid first...
47177                 
47178                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
47179                 delete cfg.grid;
47180                 if (region == 'center' && this.active ) {
47181                     cfg.background = false;
47182                 }
47183                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
47184                 
47185                 this.add(region, ret);
47186                 if (cfg.background) {
47187                     ret.on('activate', function(gp) {
47188                         if (!gp.grid.rendered) {
47189                             gp.grid.render();
47190                         }
47191                     });
47192                 } else {
47193                     grid.render();
47194                 }
47195                 break;
47196            
47197            
47198            
47199                 
47200                 
47201                 
47202             default: 
47203                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
47204                 return null;
47205              // GridPanel (grid, cfg)
47206             
47207         }
47208         this.beginUpdate();
47209         // add children..
47210         var region = '';
47211         var abn = {};
47212         Roo.each(xitems, function(i)  {
47213             region = nb && i.region ? i.region : false;
47214             
47215             var add = ret.addxtype(i);
47216            
47217             if (region) {
47218                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
47219                 if (!i.background) {
47220                     abn[region] = nb[region] ;
47221                 }
47222             }
47223             
47224         });
47225         this.endUpdate();
47226
47227         // make the last non-background panel active..
47228         //if (nb) { Roo.log(abn); }
47229         if (nb) {
47230             
47231             for(var r in abn) {
47232                 region = this.getRegion(r);
47233                 if (region) {
47234                     // tried using nb[r], but it does not work..
47235                      
47236                     region.showPanel(abn[r]);
47237                    
47238                 }
47239             }
47240         }
47241         return ret;
47242         
47243     }
47244 });
47245
47246 /**
47247  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
47248  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
47249  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
47250  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
47251  * <pre><code>
47252 // shorthand
47253 var CP = Roo.ContentPanel;
47254
47255 var layout = Roo.BorderLayout.create({
47256     north: {
47257         initialSize: 25,
47258         titlebar: false,
47259         panels: [new CP("north", "North")]
47260     },
47261     west: {
47262         split:true,
47263         initialSize: 200,
47264         minSize: 175,
47265         maxSize: 400,
47266         titlebar: true,
47267         collapsible: true,
47268         panels: [new CP("west", {title: "West"})]
47269     },
47270     east: {
47271         split:true,
47272         initialSize: 202,
47273         minSize: 175,
47274         maxSize: 400,
47275         titlebar: true,
47276         collapsible: true,
47277         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
47278     },
47279     south: {
47280         split:true,
47281         initialSize: 100,
47282         minSize: 100,
47283         maxSize: 200,
47284         titlebar: true,
47285         collapsible: true,
47286         panels: [new CP("south", {title: "South", closable: true})]
47287     },
47288     center: {
47289         titlebar: true,
47290         autoScroll:true,
47291         resizeTabs: true,
47292         minTabWidth: 50,
47293         preferredTabWidth: 150,
47294         panels: [
47295             new CP("center1", {title: "Close Me", closable: true}),
47296             new CP("center2", {title: "Center Panel", closable: false})
47297         ]
47298     }
47299 }, document.body);
47300
47301 layout.getRegion("center").showPanel("center1");
47302 </code></pre>
47303  * @param config
47304  * @param targetEl
47305  */
47306 Roo.BorderLayout.create = function(config, targetEl){
47307     var layout = new Roo.BorderLayout(targetEl || document.body, config);
47308     layout.beginUpdate();
47309     var regions = Roo.BorderLayout.RegionFactory.validRegions;
47310     for(var j = 0, jlen = regions.length; j < jlen; j++){
47311         var lr = regions[j];
47312         if(layout.regions[lr] && config[lr].panels){
47313             var r = layout.regions[lr];
47314             var ps = config[lr].panels;
47315             layout.addTypedPanels(r, ps);
47316         }
47317     }
47318     layout.endUpdate();
47319     return layout;
47320 };
47321
47322 // private
47323 Roo.BorderLayout.RegionFactory = {
47324     // private
47325     validRegions : ["north","south","east","west","center"],
47326
47327     // private
47328     create : function(target, mgr, config){
47329         target = target.toLowerCase();
47330         if(config.lightweight || config.basic){
47331             return new Roo.BasicLayoutRegion(mgr, config, target);
47332         }
47333         switch(target){
47334             case "north":
47335                 return new Roo.NorthLayoutRegion(mgr, config);
47336             case "south":
47337                 return new Roo.SouthLayoutRegion(mgr, config);
47338             case "east":
47339                 return new Roo.EastLayoutRegion(mgr, config);
47340             case "west":
47341                 return new Roo.WestLayoutRegion(mgr, config);
47342             case "center":
47343                 return new Roo.CenterLayoutRegion(mgr, config);
47344         }
47345         throw 'Layout region "'+target+'" not supported.';
47346     }
47347 };/*
47348  * Based on:
47349  * Ext JS Library 1.1.1
47350  * Copyright(c) 2006-2007, Ext JS, LLC.
47351  *
47352  * Originally Released Under LGPL - original licence link has changed is not relivant.
47353  *
47354  * Fork - LGPL
47355  * <script type="text/javascript">
47356  */
47357  
47358 /**
47359  * @class Roo.BasicLayoutRegion
47360  * @extends Roo.util.Observable
47361  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
47362  * and does not have a titlebar, tabs or any other features. All it does is size and position 
47363  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
47364  */
47365 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
47366     this.mgr = mgr;
47367     this.position  = pos;
47368     this.events = {
47369         /**
47370          * @scope Roo.BasicLayoutRegion
47371          */
47372         
47373         /**
47374          * @event beforeremove
47375          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
47376          * @param {Roo.LayoutRegion} this
47377          * @param {Roo.ContentPanel} panel The panel
47378          * @param {Object} e The cancel event object
47379          */
47380         "beforeremove" : true,
47381         /**
47382          * @event invalidated
47383          * Fires when the layout for this region is changed.
47384          * @param {Roo.LayoutRegion} this
47385          */
47386         "invalidated" : true,
47387         /**
47388          * @event visibilitychange
47389          * Fires when this region is shown or hidden 
47390          * @param {Roo.LayoutRegion} this
47391          * @param {Boolean} visibility true or false
47392          */
47393         "visibilitychange" : true,
47394         /**
47395          * @event paneladded
47396          * Fires when a panel is added. 
47397          * @param {Roo.LayoutRegion} this
47398          * @param {Roo.ContentPanel} panel The panel
47399          */
47400         "paneladded" : true,
47401         /**
47402          * @event panelremoved
47403          * Fires when a panel is removed. 
47404          * @param {Roo.LayoutRegion} this
47405          * @param {Roo.ContentPanel} panel The panel
47406          */
47407         "panelremoved" : true,
47408         /**
47409          * @event collapsed
47410          * Fires when this region is collapsed.
47411          * @param {Roo.LayoutRegion} this
47412          */
47413         "collapsed" : true,
47414         /**
47415          * @event expanded
47416          * Fires when this region is expanded.
47417          * @param {Roo.LayoutRegion} this
47418          */
47419         "expanded" : true,
47420         /**
47421          * @event slideshow
47422          * Fires when this region is slid into view.
47423          * @param {Roo.LayoutRegion} this
47424          */
47425         "slideshow" : true,
47426         /**
47427          * @event slidehide
47428          * Fires when this region slides out of view. 
47429          * @param {Roo.LayoutRegion} this
47430          */
47431         "slidehide" : true,
47432         /**
47433          * @event panelactivated
47434          * Fires when a panel is activated. 
47435          * @param {Roo.LayoutRegion} this
47436          * @param {Roo.ContentPanel} panel The activated panel
47437          */
47438         "panelactivated" : true,
47439         /**
47440          * @event resized
47441          * Fires when the user resizes this region. 
47442          * @param {Roo.LayoutRegion} this
47443          * @param {Number} newSize The new size (width for east/west, height for north/south)
47444          */
47445         "resized" : true
47446     };
47447     /** A collection of panels in this region. @type Roo.util.MixedCollection */
47448     this.panels = new Roo.util.MixedCollection();
47449     this.panels.getKey = this.getPanelId.createDelegate(this);
47450     this.box = null;
47451     this.activePanel = null;
47452     // ensure listeners are added...
47453     
47454     if (config.listeners || config.events) {
47455         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
47456             listeners : config.listeners || {},
47457             events : config.events || {}
47458         });
47459     }
47460     
47461     if(skipConfig !== true){
47462         this.applyConfig(config);
47463     }
47464 };
47465
47466 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
47467     getPanelId : function(p){
47468         return p.getId();
47469     },
47470     
47471     applyConfig : function(config){
47472         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
47473         this.config = config;
47474         
47475     },
47476     
47477     /**
47478      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
47479      * the width, for horizontal (north, south) the height.
47480      * @param {Number} newSize The new width or height
47481      */
47482     resizeTo : function(newSize){
47483         var el = this.el ? this.el :
47484                  (this.activePanel ? this.activePanel.getEl() : null);
47485         if(el){
47486             switch(this.position){
47487                 case "east":
47488                 case "west":
47489                     el.setWidth(newSize);
47490                     this.fireEvent("resized", this, newSize);
47491                 break;
47492                 case "north":
47493                 case "south":
47494                     el.setHeight(newSize);
47495                     this.fireEvent("resized", this, newSize);
47496                 break;                
47497             }
47498         }
47499     },
47500     
47501     getBox : function(){
47502         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
47503     },
47504     
47505     getMargins : function(){
47506         return this.margins;
47507     },
47508     
47509     updateBox : function(box){
47510         this.box = box;
47511         var el = this.activePanel.getEl();
47512         el.dom.style.left = box.x + "px";
47513         el.dom.style.top = box.y + "px";
47514         this.activePanel.setSize(box.width, box.height);
47515     },
47516     
47517     /**
47518      * Returns the container element for this region.
47519      * @return {Roo.Element}
47520      */
47521     getEl : function(){
47522         return this.activePanel;
47523     },
47524     
47525     /**
47526      * Returns true if this region is currently visible.
47527      * @return {Boolean}
47528      */
47529     isVisible : function(){
47530         return this.activePanel ? true : false;
47531     },
47532     
47533     setActivePanel : function(panel){
47534         panel = this.getPanel(panel);
47535         if(this.activePanel && this.activePanel != panel){
47536             this.activePanel.setActiveState(false);
47537             this.activePanel.getEl().setLeftTop(-10000,-10000);
47538         }
47539         this.activePanel = panel;
47540         panel.setActiveState(true);
47541         if(this.box){
47542             panel.setSize(this.box.width, this.box.height);
47543         }
47544         this.fireEvent("panelactivated", this, panel);
47545         this.fireEvent("invalidated");
47546     },
47547     
47548     /**
47549      * Show the specified panel.
47550      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
47551      * @return {Roo.ContentPanel} The shown panel or null
47552      */
47553     showPanel : function(panel){
47554         if(panel = this.getPanel(panel)){
47555             this.setActivePanel(panel);
47556         }
47557         return panel;
47558     },
47559     
47560     /**
47561      * Get the active panel for this region.
47562      * @return {Roo.ContentPanel} The active panel or null
47563      */
47564     getActivePanel : function(){
47565         return this.activePanel;
47566     },
47567     
47568     /**
47569      * Add the passed ContentPanel(s)
47570      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
47571      * @return {Roo.ContentPanel} The panel added (if only one was added)
47572      */
47573     add : function(panel){
47574         if(arguments.length > 1){
47575             for(var i = 0, len = arguments.length; i < len; i++) {
47576                 this.add(arguments[i]);
47577             }
47578             return null;
47579         }
47580         if(this.hasPanel(panel)){
47581             this.showPanel(panel);
47582             return panel;
47583         }
47584         var el = panel.getEl();
47585         if(el.dom.parentNode != this.mgr.el.dom){
47586             this.mgr.el.dom.appendChild(el.dom);
47587         }
47588         if(panel.setRegion){
47589             panel.setRegion(this);
47590         }
47591         this.panels.add(panel);
47592         el.setStyle("position", "absolute");
47593         if(!panel.background){
47594             this.setActivePanel(panel);
47595             if(this.config.initialSize && this.panels.getCount()==1){
47596                 this.resizeTo(this.config.initialSize);
47597             }
47598         }
47599         this.fireEvent("paneladded", this, panel);
47600         return panel;
47601     },
47602     
47603     /**
47604      * Returns true if the panel is in this region.
47605      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
47606      * @return {Boolean}
47607      */
47608     hasPanel : function(panel){
47609         if(typeof panel == "object"){ // must be panel obj
47610             panel = panel.getId();
47611         }
47612         return this.getPanel(panel) ? true : false;
47613     },
47614     
47615     /**
47616      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
47617      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
47618      * @param {Boolean} preservePanel Overrides the config preservePanel option
47619      * @return {Roo.ContentPanel} The panel that was removed
47620      */
47621     remove : function(panel, preservePanel){
47622         panel = this.getPanel(panel);
47623         if(!panel){
47624             return null;
47625         }
47626         var e = {};
47627         this.fireEvent("beforeremove", this, panel, e);
47628         if(e.cancel === true){
47629             return null;
47630         }
47631         var panelId = panel.getId();
47632         this.panels.removeKey(panelId);
47633         return panel;
47634     },
47635     
47636     /**
47637      * Returns the panel specified or null if it's not in this region.
47638      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
47639      * @return {Roo.ContentPanel}
47640      */
47641     getPanel : function(id){
47642         if(typeof id == "object"){ // must be panel obj
47643             return id;
47644         }
47645         return this.panels.get(id);
47646     },
47647     
47648     /**
47649      * Returns this regions position (north/south/east/west/center).
47650      * @return {String} 
47651      */
47652     getPosition: function(){
47653         return this.position;    
47654     }
47655 });/*
47656  * Based on:
47657  * Ext JS Library 1.1.1
47658  * Copyright(c) 2006-2007, Ext JS, LLC.
47659  *
47660  * Originally Released Under LGPL - original licence link has changed is not relivant.
47661  *
47662  * Fork - LGPL
47663  * <script type="text/javascript">
47664  */
47665  
47666 /**
47667  * @class Roo.LayoutRegion
47668  * @extends Roo.BasicLayoutRegion
47669  * This class represents a region in a layout manager.
47670  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
47671  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
47672  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
47673  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
47674  * @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})
47675  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
47676  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
47677  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
47678  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
47679  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
47680  * @cfg {String}    title           The title for the region (overrides panel titles)
47681  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
47682  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
47683  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
47684  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
47685  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
47686  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
47687  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
47688  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
47689  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
47690  * @cfg {Boolean}   showPin         True to show a pin button
47691  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
47692  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
47693  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
47694  * @cfg {Number}    width           For East/West panels
47695  * @cfg {Number}    height          For North/South panels
47696  * @cfg {Boolean}   split           To show the splitter
47697  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
47698  */
47699 Roo.LayoutRegion = function(mgr, config, pos){
47700     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
47701     var dh = Roo.DomHelper;
47702     /** This region's container element 
47703     * @type Roo.Element */
47704     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
47705     /** This region's title element 
47706     * @type Roo.Element */
47707
47708     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
47709         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
47710         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
47711     ]}, true);
47712     this.titleEl.enableDisplayMode();
47713     /** This region's title text element 
47714     * @type HTMLElement */
47715     this.titleTextEl = this.titleEl.dom.firstChild;
47716     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
47717     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
47718     this.closeBtn.enableDisplayMode();
47719     this.closeBtn.on("click", this.closeClicked, this);
47720     this.closeBtn.hide();
47721
47722     this.createBody(config);
47723     this.visible = true;
47724     this.collapsed = false;
47725
47726     if(config.hideWhenEmpty){
47727         this.hide();
47728         this.on("paneladded", this.validateVisibility, this);
47729         this.on("panelremoved", this.validateVisibility, this);
47730     }
47731     this.applyConfig(config);
47732 };
47733
47734 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
47735
47736     createBody : function(){
47737         /** This region's body element 
47738         * @type Roo.Element */
47739         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
47740     },
47741
47742     applyConfig : function(c){
47743         if(c.collapsible && this.position != "center" && !this.collapsedEl){
47744             var dh = Roo.DomHelper;
47745             if(c.titlebar !== false){
47746                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
47747                 this.collapseBtn.on("click", this.collapse, this);
47748                 this.collapseBtn.enableDisplayMode();
47749
47750                 if(c.showPin === true || this.showPin){
47751                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
47752                     this.stickBtn.enableDisplayMode();
47753                     this.stickBtn.on("click", this.expand, this);
47754                     this.stickBtn.hide();
47755                 }
47756             }
47757             /** This region's collapsed element
47758             * @type Roo.Element */
47759             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
47760                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
47761             ]}, true);
47762             if(c.floatable !== false){
47763                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
47764                this.collapsedEl.on("click", this.collapseClick, this);
47765             }
47766
47767             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
47768                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
47769                    id: "message", unselectable: "on", style:{"float":"left"}});
47770                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
47771              }
47772             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
47773             this.expandBtn.on("click", this.expand, this);
47774         }
47775         if(this.collapseBtn){
47776             this.collapseBtn.setVisible(c.collapsible == true);
47777         }
47778         this.cmargins = c.cmargins || this.cmargins ||
47779                          (this.position == "west" || this.position == "east" ?
47780                              {top: 0, left: 2, right:2, bottom: 0} :
47781                              {top: 2, left: 0, right:0, bottom: 2});
47782         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
47783         this.bottomTabs = c.tabPosition != "top";
47784         this.autoScroll = c.autoScroll || false;
47785         if(this.autoScroll){
47786             this.bodyEl.setStyle("overflow", "auto");
47787         }else{
47788             this.bodyEl.setStyle("overflow", "hidden");
47789         }
47790         //if(c.titlebar !== false){
47791             if((!c.titlebar && !c.title) || c.titlebar === false){
47792                 this.titleEl.hide();
47793             }else{
47794                 this.titleEl.show();
47795                 if(c.title){
47796                     this.titleTextEl.innerHTML = c.title;
47797                 }
47798             }
47799         //}
47800         this.duration = c.duration || .30;
47801         this.slideDuration = c.slideDuration || .45;
47802         this.config = c;
47803         if(c.collapsed){
47804             this.collapse(true);
47805         }
47806         if(c.hidden){
47807             this.hide();
47808         }
47809     },
47810     /**
47811      * Returns true if this region is currently visible.
47812      * @return {Boolean}
47813      */
47814     isVisible : function(){
47815         return this.visible;
47816     },
47817
47818     /**
47819      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
47820      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
47821      */
47822     setCollapsedTitle : function(title){
47823         title = title || "&#160;";
47824         if(this.collapsedTitleTextEl){
47825             this.collapsedTitleTextEl.innerHTML = title;
47826         }
47827     },
47828
47829     getBox : function(){
47830         var b;
47831         if(!this.collapsed){
47832             b = this.el.getBox(false, true);
47833         }else{
47834             b = this.collapsedEl.getBox(false, true);
47835         }
47836         return b;
47837     },
47838
47839     getMargins : function(){
47840         return this.collapsed ? this.cmargins : this.margins;
47841     },
47842
47843     highlight : function(){
47844         this.el.addClass("x-layout-panel-dragover");
47845     },
47846
47847     unhighlight : function(){
47848         this.el.removeClass("x-layout-panel-dragover");
47849     },
47850
47851     updateBox : function(box){
47852         this.box = box;
47853         if(!this.collapsed){
47854             this.el.dom.style.left = box.x + "px";
47855             this.el.dom.style.top = box.y + "px";
47856             this.updateBody(box.width, box.height);
47857         }else{
47858             this.collapsedEl.dom.style.left = box.x + "px";
47859             this.collapsedEl.dom.style.top = box.y + "px";
47860             this.collapsedEl.setSize(box.width, box.height);
47861         }
47862         if(this.tabs){
47863             this.tabs.autoSizeTabs();
47864         }
47865     },
47866
47867     updateBody : function(w, h){
47868         if(w !== null){
47869             this.el.setWidth(w);
47870             w -= this.el.getBorderWidth("rl");
47871             if(this.config.adjustments){
47872                 w += this.config.adjustments[0];
47873             }
47874         }
47875         if(h !== null){
47876             this.el.setHeight(h);
47877             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
47878             h -= this.el.getBorderWidth("tb");
47879             if(this.config.adjustments){
47880                 h += this.config.adjustments[1];
47881             }
47882             this.bodyEl.setHeight(h);
47883             if(this.tabs){
47884                 h = this.tabs.syncHeight(h);
47885             }
47886         }
47887         if(this.panelSize){
47888             w = w !== null ? w : this.panelSize.width;
47889             h = h !== null ? h : this.panelSize.height;
47890         }
47891         if(this.activePanel){
47892             var el = this.activePanel.getEl();
47893             w = w !== null ? w : el.getWidth();
47894             h = h !== null ? h : el.getHeight();
47895             this.panelSize = {width: w, height: h};
47896             this.activePanel.setSize(w, h);
47897         }
47898         if(Roo.isIE && this.tabs){
47899             this.tabs.el.repaint();
47900         }
47901     },
47902
47903     /**
47904      * Returns the container element for this region.
47905      * @return {Roo.Element}
47906      */
47907     getEl : function(){
47908         return this.el;
47909     },
47910
47911     /**
47912      * Hides this region.
47913      */
47914     hide : function(){
47915         if(!this.collapsed){
47916             this.el.dom.style.left = "-2000px";
47917             this.el.hide();
47918         }else{
47919             this.collapsedEl.dom.style.left = "-2000px";
47920             this.collapsedEl.hide();
47921         }
47922         this.visible = false;
47923         this.fireEvent("visibilitychange", this, false);
47924     },
47925
47926     /**
47927      * Shows this region if it was previously hidden.
47928      */
47929     show : function(){
47930         if(!this.collapsed){
47931             this.el.show();
47932         }else{
47933             this.collapsedEl.show();
47934         }
47935         this.visible = true;
47936         this.fireEvent("visibilitychange", this, true);
47937     },
47938
47939     closeClicked : function(){
47940         if(this.activePanel){
47941             this.remove(this.activePanel);
47942         }
47943     },
47944
47945     collapseClick : function(e){
47946         if(this.isSlid){
47947            e.stopPropagation();
47948            this.slideIn();
47949         }else{
47950            e.stopPropagation();
47951            this.slideOut();
47952         }
47953     },
47954
47955     /**
47956      * Collapses this region.
47957      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
47958      */
47959     collapse : function(skipAnim){
47960         if(this.collapsed) return;
47961         this.collapsed = true;
47962         if(this.split){
47963             this.split.el.hide();
47964         }
47965         if(this.config.animate && skipAnim !== true){
47966             this.fireEvent("invalidated", this);
47967             this.animateCollapse();
47968         }else{
47969             this.el.setLocation(-20000,-20000);
47970             this.el.hide();
47971             this.collapsedEl.show();
47972             this.fireEvent("collapsed", this);
47973             this.fireEvent("invalidated", this);
47974         }
47975     },
47976
47977     animateCollapse : function(){
47978         // overridden
47979     },
47980
47981     /**
47982      * Expands this region if it was previously collapsed.
47983      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
47984      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
47985      */
47986     expand : function(e, skipAnim){
47987         if(e) e.stopPropagation();
47988         if(!this.collapsed || this.el.hasActiveFx()) return;
47989         if(this.isSlid){
47990             this.afterSlideIn();
47991             skipAnim = true;
47992         }
47993         this.collapsed = false;
47994         if(this.config.animate && skipAnim !== true){
47995             this.animateExpand();
47996         }else{
47997             this.el.show();
47998             if(this.split){
47999                 this.split.el.show();
48000             }
48001             this.collapsedEl.setLocation(-2000,-2000);
48002             this.collapsedEl.hide();
48003             this.fireEvent("invalidated", this);
48004             this.fireEvent("expanded", this);
48005         }
48006     },
48007
48008     animateExpand : function(){
48009         // overridden
48010     },
48011
48012     initTabs : function()
48013     {
48014         this.bodyEl.setStyle("overflow", "hidden");
48015         var ts = new Roo.TabPanel(
48016                 this.bodyEl.dom,
48017                 {
48018                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
48019                     disableTooltips: this.config.disableTabTips,
48020                     toolbar : this.config.toolbar
48021                 }
48022         );
48023         if(this.config.hideTabs){
48024             ts.stripWrap.setDisplayed(false);
48025         }
48026         this.tabs = ts;
48027         ts.resizeTabs = this.config.resizeTabs === true;
48028         ts.minTabWidth = this.config.minTabWidth || 40;
48029         ts.maxTabWidth = this.config.maxTabWidth || 250;
48030         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
48031         ts.monitorResize = false;
48032         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
48033         ts.bodyEl.addClass('x-layout-tabs-body');
48034         this.panels.each(this.initPanelAsTab, this);
48035     },
48036
48037     initPanelAsTab : function(panel){
48038         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
48039                     this.config.closeOnTab && panel.isClosable());
48040         if(panel.tabTip !== undefined){
48041             ti.setTooltip(panel.tabTip);
48042         }
48043         ti.on("activate", function(){
48044               this.setActivePanel(panel);
48045         }, this);
48046         if(this.config.closeOnTab){
48047             ti.on("beforeclose", function(t, e){
48048                 e.cancel = true;
48049                 this.remove(panel);
48050             }, this);
48051         }
48052         return ti;
48053     },
48054
48055     updatePanelTitle : function(panel, title){
48056         if(this.activePanel == panel){
48057             this.updateTitle(title);
48058         }
48059         if(this.tabs){
48060             var ti = this.tabs.getTab(panel.getEl().id);
48061             ti.setText(title);
48062             if(panel.tabTip !== undefined){
48063                 ti.setTooltip(panel.tabTip);
48064             }
48065         }
48066     },
48067
48068     updateTitle : function(title){
48069         if(this.titleTextEl && !this.config.title){
48070             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
48071         }
48072     },
48073
48074     setActivePanel : function(panel){
48075         panel = this.getPanel(panel);
48076         if(this.activePanel && this.activePanel != panel){
48077             this.activePanel.setActiveState(false);
48078         }
48079         this.activePanel = panel;
48080         panel.setActiveState(true);
48081         if(this.panelSize){
48082             panel.setSize(this.panelSize.width, this.panelSize.height);
48083         }
48084         if(this.closeBtn){
48085             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
48086         }
48087         this.updateTitle(panel.getTitle());
48088         if(this.tabs){
48089             this.fireEvent("invalidated", this);
48090         }
48091         this.fireEvent("panelactivated", this, panel);
48092     },
48093
48094     /**
48095      * Shows the specified panel.
48096      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
48097      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
48098      */
48099     showPanel : function(panel){
48100         if(panel = this.getPanel(panel)){
48101             if(this.tabs){
48102                 var tab = this.tabs.getTab(panel.getEl().id);
48103                 if(tab.isHidden()){
48104                     this.tabs.unhideTab(tab.id);
48105                 }
48106                 tab.activate();
48107             }else{
48108                 this.setActivePanel(panel);
48109             }
48110         }
48111         return panel;
48112     },
48113
48114     /**
48115      * Get the active panel for this region.
48116      * @return {Roo.ContentPanel} The active panel or null
48117      */
48118     getActivePanel : function(){
48119         return this.activePanel;
48120     },
48121
48122     validateVisibility : function(){
48123         if(this.panels.getCount() < 1){
48124             this.updateTitle("&#160;");
48125             this.closeBtn.hide();
48126             this.hide();
48127         }else{
48128             if(!this.isVisible()){
48129                 this.show();
48130             }
48131         }
48132     },
48133
48134     /**
48135      * Adds the passed ContentPanel(s) to this region.
48136      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
48137      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
48138      */
48139     add : function(panel){
48140         if(arguments.length > 1){
48141             for(var i = 0, len = arguments.length; i < len; i++) {
48142                 this.add(arguments[i]);
48143             }
48144             return null;
48145         }
48146         if(this.hasPanel(panel)){
48147             this.showPanel(panel);
48148             return panel;
48149         }
48150         panel.setRegion(this);
48151         this.panels.add(panel);
48152         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
48153             this.bodyEl.dom.appendChild(panel.getEl().dom);
48154             if(panel.background !== true){
48155                 this.setActivePanel(panel);
48156             }
48157             this.fireEvent("paneladded", this, panel);
48158             return panel;
48159         }
48160         if(!this.tabs){
48161             this.initTabs();
48162         }else{
48163             this.initPanelAsTab(panel);
48164         }
48165         if(panel.background !== true){
48166             this.tabs.activate(panel.getEl().id);
48167         }
48168         this.fireEvent("paneladded", this, panel);
48169         return panel;
48170     },
48171
48172     /**
48173      * Hides the tab for the specified panel.
48174      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
48175      */
48176     hidePanel : function(panel){
48177         if(this.tabs && (panel = this.getPanel(panel))){
48178             this.tabs.hideTab(panel.getEl().id);
48179         }
48180     },
48181
48182     /**
48183      * Unhides the tab for a previously hidden panel.
48184      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
48185      */
48186     unhidePanel : function(panel){
48187         if(this.tabs && (panel = this.getPanel(panel))){
48188             this.tabs.unhideTab(panel.getEl().id);
48189         }
48190     },
48191
48192     clearPanels : function(){
48193         while(this.panels.getCount() > 0){
48194              this.remove(this.panels.first());
48195         }
48196     },
48197
48198     /**
48199      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
48200      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
48201      * @param {Boolean} preservePanel Overrides the config preservePanel option
48202      * @return {Roo.ContentPanel} The panel that was removed
48203      */
48204     remove : function(panel, preservePanel){
48205         panel = this.getPanel(panel);
48206         if(!panel){
48207             return null;
48208         }
48209         var e = {};
48210         this.fireEvent("beforeremove", this, panel, e);
48211         if(e.cancel === true){
48212             return null;
48213         }
48214         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
48215         var panelId = panel.getId();
48216         this.panels.removeKey(panelId);
48217         if(preservePanel){
48218             document.body.appendChild(panel.getEl().dom);
48219         }
48220         if(this.tabs){
48221             this.tabs.removeTab(panel.getEl().id);
48222         }else if (!preservePanel){
48223             this.bodyEl.dom.removeChild(panel.getEl().dom);
48224         }
48225         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
48226             var p = this.panels.first();
48227             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
48228             tempEl.appendChild(p.getEl().dom);
48229             this.bodyEl.update("");
48230             this.bodyEl.dom.appendChild(p.getEl().dom);
48231             tempEl = null;
48232             this.updateTitle(p.getTitle());
48233             this.tabs = null;
48234             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
48235             this.setActivePanel(p);
48236         }
48237         panel.setRegion(null);
48238         if(this.activePanel == panel){
48239             this.activePanel = null;
48240         }
48241         if(this.config.autoDestroy !== false && preservePanel !== true){
48242             try{panel.destroy();}catch(e){}
48243         }
48244         this.fireEvent("panelremoved", this, panel);
48245         return panel;
48246     },
48247
48248     /**
48249      * Returns the TabPanel component used by this region
48250      * @return {Roo.TabPanel}
48251      */
48252     getTabs : function(){
48253         return this.tabs;
48254     },
48255
48256     createTool : function(parentEl, className){
48257         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
48258             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
48259         btn.addClassOnOver("x-layout-tools-button-over");
48260         return btn;
48261     }
48262 });/*
48263  * Based on:
48264  * Ext JS Library 1.1.1
48265  * Copyright(c) 2006-2007, Ext JS, LLC.
48266  *
48267  * Originally Released Under LGPL - original licence link has changed is not relivant.
48268  *
48269  * Fork - LGPL
48270  * <script type="text/javascript">
48271  */
48272  
48273
48274
48275 /**
48276  * @class Roo.SplitLayoutRegion
48277  * @extends Roo.LayoutRegion
48278  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
48279  */
48280 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
48281     this.cursor = cursor;
48282     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
48283 };
48284
48285 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
48286     splitTip : "Drag to resize.",
48287     collapsibleSplitTip : "Drag to resize. Double click to hide.",
48288     useSplitTips : false,
48289
48290     applyConfig : function(config){
48291         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
48292         if(config.split){
48293             if(!this.split){
48294                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
48295                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
48296                 /** The SplitBar for this region 
48297                 * @type Roo.SplitBar */
48298                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
48299                 this.split.on("moved", this.onSplitMove, this);
48300                 this.split.useShim = config.useShim === true;
48301                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
48302                 if(this.useSplitTips){
48303                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
48304                 }
48305                 if(config.collapsible){
48306                     this.split.el.on("dblclick", this.collapse,  this);
48307                 }
48308             }
48309             if(typeof config.minSize != "undefined"){
48310                 this.split.minSize = config.minSize;
48311             }
48312             if(typeof config.maxSize != "undefined"){
48313                 this.split.maxSize = config.maxSize;
48314             }
48315             if(config.hideWhenEmpty || config.hidden || config.collapsed){
48316                 this.hideSplitter();
48317             }
48318         }
48319     },
48320
48321     getHMaxSize : function(){
48322          var cmax = this.config.maxSize || 10000;
48323          var center = this.mgr.getRegion("center");
48324          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
48325     },
48326
48327     getVMaxSize : function(){
48328          var cmax = this.config.maxSize || 10000;
48329          var center = this.mgr.getRegion("center");
48330          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
48331     },
48332
48333     onSplitMove : function(split, newSize){
48334         this.fireEvent("resized", this, newSize);
48335     },
48336     
48337     /** 
48338      * Returns the {@link Roo.SplitBar} for this region.
48339      * @return {Roo.SplitBar}
48340      */
48341     getSplitBar : function(){
48342         return this.split;
48343     },
48344     
48345     hide : function(){
48346         this.hideSplitter();
48347         Roo.SplitLayoutRegion.superclass.hide.call(this);
48348     },
48349
48350     hideSplitter : function(){
48351         if(this.split){
48352             this.split.el.setLocation(-2000,-2000);
48353             this.split.el.hide();
48354         }
48355     },
48356
48357     show : function(){
48358         if(this.split){
48359             this.split.el.show();
48360         }
48361         Roo.SplitLayoutRegion.superclass.show.call(this);
48362     },
48363     
48364     beforeSlide: function(){
48365         if(Roo.isGecko){// firefox overflow auto bug workaround
48366             this.bodyEl.clip();
48367             if(this.tabs) this.tabs.bodyEl.clip();
48368             if(this.activePanel){
48369                 this.activePanel.getEl().clip();
48370                 
48371                 if(this.activePanel.beforeSlide){
48372                     this.activePanel.beforeSlide();
48373                 }
48374             }
48375         }
48376     },
48377     
48378     afterSlide : function(){
48379         if(Roo.isGecko){// firefox overflow auto bug workaround
48380             this.bodyEl.unclip();
48381             if(this.tabs) this.tabs.bodyEl.unclip();
48382             if(this.activePanel){
48383                 this.activePanel.getEl().unclip();
48384                 if(this.activePanel.afterSlide){
48385                     this.activePanel.afterSlide();
48386                 }
48387             }
48388         }
48389     },
48390
48391     initAutoHide : function(){
48392         if(this.autoHide !== false){
48393             if(!this.autoHideHd){
48394                 var st = new Roo.util.DelayedTask(this.slideIn, this);
48395                 this.autoHideHd = {
48396                     "mouseout": function(e){
48397                         if(!e.within(this.el, true)){
48398                             st.delay(500);
48399                         }
48400                     },
48401                     "mouseover" : function(e){
48402                         st.cancel();
48403                     },
48404                     scope : this
48405                 };
48406             }
48407             this.el.on(this.autoHideHd);
48408         }
48409     },
48410
48411     clearAutoHide : function(){
48412         if(this.autoHide !== false){
48413             this.el.un("mouseout", this.autoHideHd.mouseout);
48414             this.el.un("mouseover", this.autoHideHd.mouseover);
48415         }
48416     },
48417
48418     clearMonitor : function(){
48419         Roo.get(document).un("click", this.slideInIf, this);
48420     },
48421
48422     // these names are backwards but not changed for compat
48423     slideOut : function(){
48424         if(this.isSlid || this.el.hasActiveFx()){
48425             return;
48426         }
48427         this.isSlid = true;
48428         if(this.collapseBtn){
48429             this.collapseBtn.hide();
48430         }
48431         this.closeBtnState = this.closeBtn.getStyle('display');
48432         this.closeBtn.hide();
48433         if(this.stickBtn){
48434             this.stickBtn.show();
48435         }
48436         this.el.show();
48437         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
48438         this.beforeSlide();
48439         this.el.setStyle("z-index", 10001);
48440         this.el.slideIn(this.getSlideAnchor(), {
48441             callback: function(){
48442                 this.afterSlide();
48443                 this.initAutoHide();
48444                 Roo.get(document).on("click", this.slideInIf, this);
48445                 this.fireEvent("slideshow", this);
48446             },
48447             scope: this,
48448             block: true
48449         });
48450     },
48451
48452     afterSlideIn : function(){
48453         this.clearAutoHide();
48454         this.isSlid = false;
48455         this.clearMonitor();
48456         this.el.setStyle("z-index", "");
48457         if(this.collapseBtn){
48458             this.collapseBtn.show();
48459         }
48460         this.closeBtn.setStyle('display', this.closeBtnState);
48461         if(this.stickBtn){
48462             this.stickBtn.hide();
48463         }
48464         this.fireEvent("slidehide", this);
48465     },
48466
48467     slideIn : function(cb){
48468         if(!this.isSlid || this.el.hasActiveFx()){
48469             Roo.callback(cb);
48470             return;
48471         }
48472         this.isSlid = false;
48473         this.beforeSlide();
48474         this.el.slideOut(this.getSlideAnchor(), {
48475             callback: function(){
48476                 this.el.setLeftTop(-10000, -10000);
48477                 this.afterSlide();
48478                 this.afterSlideIn();
48479                 Roo.callback(cb);
48480             },
48481             scope: this,
48482             block: true
48483         });
48484     },
48485     
48486     slideInIf : function(e){
48487         if(!e.within(this.el)){
48488             this.slideIn();
48489         }
48490     },
48491
48492     animateCollapse : function(){
48493         this.beforeSlide();
48494         this.el.setStyle("z-index", 20000);
48495         var anchor = this.getSlideAnchor();
48496         this.el.slideOut(anchor, {
48497             callback : function(){
48498                 this.el.setStyle("z-index", "");
48499                 this.collapsedEl.slideIn(anchor, {duration:.3});
48500                 this.afterSlide();
48501                 this.el.setLocation(-10000,-10000);
48502                 this.el.hide();
48503                 this.fireEvent("collapsed", this);
48504             },
48505             scope: this,
48506             block: true
48507         });
48508     },
48509
48510     animateExpand : function(){
48511         this.beforeSlide();
48512         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
48513         this.el.setStyle("z-index", 20000);
48514         this.collapsedEl.hide({
48515             duration:.1
48516         });
48517         this.el.slideIn(this.getSlideAnchor(), {
48518             callback : function(){
48519                 this.el.setStyle("z-index", "");
48520                 this.afterSlide();
48521                 if(this.split){
48522                     this.split.el.show();
48523                 }
48524                 this.fireEvent("invalidated", this);
48525                 this.fireEvent("expanded", this);
48526             },
48527             scope: this,
48528             block: true
48529         });
48530     },
48531
48532     anchors : {
48533         "west" : "left",
48534         "east" : "right",
48535         "north" : "top",
48536         "south" : "bottom"
48537     },
48538
48539     sanchors : {
48540         "west" : "l",
48541         "east" : "r",
48542         "north" : "t",
48543         "south" : "b"
48544     },
48545
48546     canchors : {
48547         "west" : "tl-tr",
48548         "east" : "tr-tl",
48549         "north" : "tl-bl",
48550         "south" : "bl-tl"
48551     },
48552
48553     getAnchor : function(){
48554         return this.anchors[this.position];
48555     },
48556
48557     getCollapseAnchor : function(){
48558         return this.canchors[this.position];
48559     },
48560
48561     getSlideAnchor : function(){
48562         return this.sanchors[this.position];
48563     },
48564
48565     getAlignAdj : function(){
48566         var cm = this.cmargins;
48567         switch(this.position){
48568             case "west":
48569                 return [0, 0];
48570             break;
48571             case "east":
48572                 return [0, 0];
48573             break;
48574             case "north":
48575                 return [0, 0];
48576             break;
48577             case "south":
48578                 return [0, 0];
48579             break;
48580         }
48581     },
48582
48583     getExpandAdj : function(){
48584         var c = this.collapsedEl, cm = this.cmargins;
48585         switch(this.position){
48586             case "west":
48587                 return [-(cm.right+c.getWidth()+cm.left), 0];
48588             break;
48589             case "east":
48590                 return [cm.right+c.getWidth()+cm.left, 0];
48591             break;
48592             case "north":
48593                 return [0, -(cm.top+cm.bottom+c.getHeight())];
48594             break;
48595             case "south":
48596                 return [0, cm.top+cm.bottom+c.getHeight()];
48597             break;
48598         }
48599     }
48600 });/*
48601  * Based on:
48602  * Ext JS Library 1.1.1
48603  * Copyright(c) 2006-2007, Ext JS, LLC.
48604  *
48605  * Originally Released Under LGPL - original licence link has changed is not relivant.
48606  *
48607  * Fork - LGPL
48608  * <script type="text/javascript">
48609  */
48610 /*
48611  * These classes are private internal classes
48612  */
48613 Roo.CenterLayoutRegion = function(mgr, config){
48614     Roo.LayoutRegion.call(this, mgr, config, "center");
48615     this.visible = true;
48616     this.minWidth = config.minWidth || 20;
48617     this.minHeight = config.minHeight || 20;
48618 };
48619
48620 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
48621     hide : function(){
48622         // center panel can't be hidden
48623     },
48624     
48625     show : function(){
48626         // center panel can't be hidden
48627     },
48628     
48629     getMinWidth: function(){
48630         return this.minWidth;
48631     },
48632     
48633     getMinHeight: function(){
48634         return this.minHeight;
48635     }
48636 });
48637
48638
48639 Roo.NorthLayoutRegion = function(mgr, config){
48640     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
48641     if(this.split){
48642         this.split.placement = Roo.SplitBar.TOP;
48643         this.split.orientation = Roo.SplitBar.VERTICAL;
48644         this.split.el.addClass("x-layout-split-v");
48645     }
48646     var size = config.initialSize || config.height;
48647     if(typeof size != "undefined"){
48648         this.el.setHeight(size);
48649     }
48650 };
48651 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
48652     orientation: Roo.SplitBar.VERTICAL,
48653     getBox : function(){
48654         if(this.collapsed){
48655             return this.collapsedEl.getBox();
48656         }
48657         var box = this.el.getBox();
48658         if(this.split){
48659             box.height += this.split.el.getHeight();
48660         }
48661         return box;
48662     },
48663     
48664     updateBox : function(box){
48665         if(this.split && !this.collapsed){
48666             box.height -= this.split.el.getHeight();
48667             this.split.el.setLeft(box.x);
48668             this.split.el.setTop(box.y+box.height);
48669             this.split.el.setWidth(box.width);
48670         }
48671         if(this.collapsed){
48672             this.updateBody(box.width, null);
48673         }
48674         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48675     }
48676 });
48677
48678 Roo.SouthLayoutRegion = function(mgr, config){
48679     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
48680     if(this.split){
48681         this.split.placement = Roo.SplitBar.BOTTOM;
48682         this.split.orientation = Roo.SplitBar.VERTICAL;
48683         this.split.el.addClass("x-layout-split-v");
48684     }
48685     var size = config.initialSize || config.height;
48686     if(typeof size != "undefined"){
48687         this.el.setHeight(size);
48688     }
48689 };
48690 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
48691     orientation: Roo.SplitBar.VERTICAL,
48692     getBox : function(){
48693         if(this.collapsed){
48694             return this.collapsedEl.getBox();
48695         }
48696         var box = this.el.getBox();
48697         if(this.split){
48698             var sh = this.split.el.getHeight();
48699             box.height += sh;
48700             box.y -= sh;
48701         }
48702         return box;
48703     },
48704     
48705     updateBox : function(box){
48706         if(this.split && !this.collapsed){
48707             var sh = this.split.el.getHeight();
48708             box.height -= sh;
48709             box.y += sh;
48710             this.split.el.setLeft(box.x);
48711             this.split.el.setTop(box.y-sh);
48712             this.split.el.setWidth(box.width);
48713         }
48714         if(this.collapsed){
48715             this.updateBody(box.width, null);
48716         }
48717         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48718     }
48719 });
48720
48721 Roo.EastLayoutRegion = function(mgr, config){
48722     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
48723     if(this.split){
48724         this.split.placement = Roo.SplitBar.RIGHT;
48725         this.split.orientation = Roo.SplitBar.HORIZONTAL;
48726         this.split.el.addClass("x-layout-split-h");
48727     }
48728     var size = config.initialSize || config.width;
48729     if(typeof size != "undefined"){
48730         this.el.setWidth(size);
48731     }
48732 };
48733 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
48734     orientation: Roo.SplitBar.HORIZONTAL,
48735     getBox : function(){
48736         if(this.collapsed){
48737             return this.collapsedEl.getBox();
48738         }
48739         var box = this.el.getBox();
48740         if(this.split){
48741             var sw = this.split.el.getWidth();
48742             box.width += sw;
48743             box.x -= sw;
48744         }
48745         return box;
48746     },
48747
48748     updateBox : function(box){
48749         if(this.split && !this.collapsed){
48750             var sw = this.split.el.getWidth();
48751             box.width -= sw;
48752             this.split.el.setLeft(box.x);
48753             this.split.el.setTop(box.y);
48754             this.split.el.setHeight(box.height);
48755             box.x += sw;
48756         }
48757         if(this.collapsed){
48758             this.updateBody(null, box.height);
48759         }
48760         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48761     }
48762 });
48763
48764 Roo.WestLayoutRegion = function(mgr, config){
48765     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
48766     if(this.split){
48767         this.split.placement = Roo.SplitBar.LEFT;
48768         this.split.orientation = Roo.SplitBar.HORIZONTAL;
48769         this.split.el.addClass("x-layout-split-h");
48770     }
48771     var size = config.initialSize || config.width;
48772     if(typeof size != "undefined"){
48773         this.el.setWidth(size);
48774     }
48775 };
48776 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
48777     orientation: Roo.SplitBar.HORIZONTAL,
48778     getBox : function(){
48779         if(this.collapsed){
48780             return this.collapsedEl.getBox();
48781         }
48782         var box = this.el.getBox();
48783         if(this.split){
48784             box.width += this.split.el.getWidth();
48785         }
48786         return box;
48787     },
48788     
48789     updateBox : function(box){
48790         if(this.split && !this.collapsed){
48791             var sw = this.split.el.getWidth();
48792             box.width -= sw;
48793             this.split.el.setLeft(box.x+box.width);
48794             this.split.el.setTop(box.y);
48795             this.split.el.setHeight(box.height);
48796         }
48797         if(this.collapsed){
48798             this.updateBody(null, box.height);
48799         }
48800         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48801     }
48802 });
48803 /*
48804  * Based on:
48805  * Ext JS Library 1.1.1
48806  * Copyright(c) 2006-2007, Ext JS, LLC.
48807  *
48808  * Originally Released Under LGPL - original licence link has changed is not relivant.
48809  *
48810  * Fork - LGPL
48811  * <script type="text/javascript">
48812  */
48813  
48814  
48815 /*
48816  * Private internal class for reading and applying state
48817  */
48818 Roo.LayoutStateManager = function(layout){
48819      // default empty state
48820      this.state = {
48821         north: {},
48822         south: {},
48823         east: {},
48824         west: {}       
48825     };
48826 };
48827
48828 Roo.LayoutStateManager.prototype = {
48829     init : function(layout, provider){
48830         this.provider = provider;
48831         var state = provider.get(layout.id+"-layout-state");
48832         if(state){
48833             var wasUpdating = layout.isUpdating();
48834             if(!wasUpdating){
48835                 layout.beginUpdate();
48836             }
48837             for(var key in state){
48838                 if(typeof state[key] != "function"){
48839                     var rstate = state[key];
48840                     var r = layout.getRegion(key);
48841                     if(r && rstate){
48842                         if(rstate.size){
48843                             r.resizeTo(rstate.size);
48844                         }
48845                         if(rstate.collapsed == true){
48846                             r.collapse(true);
48847                         }else{
48848                             r.expand(null, true);
48849                         }
48850                     }
48851                 }
48852             }
48853             if(!wasUpdating){
48854                 layout.endUpdate();
48855             }
48856             this.state = state; 
48857         }
48858         this.layout = layout;
48859         layout.on("regionresized", this.onRegionResized, this);
48860         layout.on("regioncollapsed", this.onRegionCollapsed, this);
48861         layout.on("regionexpanded", this.onRegionExpanded, this);
48862     },
48863     
48864     storeState : function(){
48865         this.provider.set(this.layout.id+"-layout-state", this.state);
48866     },
48867     
48868     onRegionResized : function(region, newSize){
48869         this.state[region.getPosition()].size = newSize;
48870         this.storeState();
48871     },
48872     
48873     onRegionCollapsed : function(region){
48874         this.state[region.getPosition()].collapsed = true;
48875         this.storeState();
48876     },
48877     
48878     onRegionExpanded : function(region){
48879         this.state[region.getPosition()].collapsed = false;
48880         this.storeState();
48881     }
48882 };/*
48883  * Based on:
48884  * Ext JS Library 1.1.1
48885  * Copyright(c) 2006-2007, Ext JS, LLC.
48886  *
48887  * Originally Released Under LGPL - original licence link has changed is not relivant.
48888  *
48889  * Fork - LGPL
48890  * <script type="text/javascript">
48891  */
48892 /**
48893  * @class Roo.ContentPanel
48894  * @extends Roo.util.Observable
48895  * A basic ContentPanel element.
48896  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
48897  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
48898  * @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
48899  * @cfg {Boolean}   closable      True if the panel can be closed/removed
48900  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
48901  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
48902  * @cfg {Toolbar}   toolbar       A toolbar for this panel
48903  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
48904  * @cfg {String} title          The title for this panel
48905  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
48906  * @cfg {String} url            Calls {@link #setUrl} with this value
48907  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
48908  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
48909  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
48910  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
48911
48912  * @constructor
48913  * Create a new ContentPanel.
48914  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
48915  * @param {String/Object} config A string to set only the title or a config object
48916  * @param {String} content (optional) Set the HTML content for this panel
48917  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
48918  */
48919 Roo.ContentPanel = function(el, config, content){
48920     
48921      
48922     /*
48923     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
48924         config = el;
48925         el = Roo.id();
48926     }
48927     if (config && config.parentLayout) { 
48928         el = config.parentLayout.el.createChild(); 
48929     }
48930     */
48931     if(el.autoCreate){ // xtype is available if this is called from factory
48932         config = el;
48933         el = Roo.id();
48934     }
48935     this.el = Roo.get(el);
48936     if(!this.el && config && config.autoCreate){
48937         if(typeof config.autoCreate == "object"){
48938             if(!config.autoCreate.id){
48939                 config.autoCreate.id = config.id||el;
48940             }
48941             this.el = Roo.DomHelper.append(document.body,
48942                         config.autoCreate, true);
48943         }else{
48944             this.el = Roo.DomHelper.append(document.body,
48945                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
48946         }
48947     }
48948     this.closable = false;
48949     this.loaded = false;
48950     this.active = false;
48951     if(typeof config == "string"){
48952         this.title = config;
48953     }else{
48954         Roo.apply(this, config);
48955     }
48956     
48957     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
48958         this.wrapEl = this.el.wrap();
48959         this.toolbar.container = this.el.insertSibling(false, 'before');
48960         this.toolbar = new Roo.Toolbar(this.toolbar);
48961     }
48962     
48963     // xtype created footer. - not sure if will work as we normally have to render first..
48964     if (this.footer && !this.footer.el && this.footer.xtype) {
48965         if (!this.wrapEl) {
48966             this.wrapEl = this.el.wrap();
48967         }
48968     
48969         this.footer.container = this.wrapEl.createChild();
48970          
48971         this.footer = Roo.factory(this.footer, Roo);
48972         
48973     }
48974     
48975     if(this.resizeEl){
48976         this.resizeEl = Roo.get(this.resizeEl, true);
48977     }else{
48978         this.resizeEl = this.el;
48979     }
48980     // handle view.xtype
48981     
48982  
48983     
48984     
48985     this.addEvents({
48986         /**
48987          * @event activate
48988          * Fires when this panel is activated. 
48989          * @param {Roo.ContentPanel} this
48990          */
48991         "activate" : true,
48992         /**
48993          * @event deactivate
48994          * Fires when this panel is activated. 
48995          * @param {Roo.ContentPanel} this
48996          */
48997         "deactivate" : true,
48998
48999         /**
49000          * @event resize
49001          * Fires when this panel is resized if fitToFrame is true.
49002          * @param {Roo.ContentPanel} this
49003          * @param {Number} width The width after any component adjustments
49004          * @param {Number} height The height after any component adjustments
49005          */
49006         "resize" : true,
49007         
49008          /**
49009          * @event render
49010          * Fires when this tab is created
49011          * @param {Roo.ContentPanel} this
49012          */
49013         "render" : true
49014         
49015         
49016         
49017     });
49018     
49019
49020     
49021     
49022     if(this.autoScroll){
49023         this.resizeEl.setStyle("overflow", "auto");
49024     } else {
49025         // fix randome scrolling
49026         this.el.on('scroll', function() {
49027             Roo.log('fix random scolling');
49028             this.scrollTo('top',0); 
49029         });
49030     }
49031     content = content || this.content;
49032     if(content){
49033         this.setContent(content);
49034     }
49035     if(config && config.url){
49036         this.setUrl(this.url, this.params, this.loadOnce);
49037     }
49038     
49039     
49040     
49041     Roo.ContentPanel.superclass.constructor.call(this);
49042     
49043     if (this.view && typeof(this.view.xtype) != 'undefined') {
49044         this.view.el = this.el.appendChild(document.createElement("div"));
49045         this.view = Roo.factory(this.view); 
49046         this.view.render  &&  this.view.render(false, '');  
49047     }
49048     
49049     
49050     this.fireEvent('render', this);
49051 };
49052
49053 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
49054     tabTip:'',
49055     setRegion : function(region){
49056         this.region = region;
49057         if(region){
49058            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
49059         }else{
49060            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
49061         } 
49062     },
49063     
49064     /**
49065      * Returns the toolbar for this Panel if one was configured. 
49066      * @return {Roo.Toolbar} 
49067      */
49068     getToolbar : function(){
49069         return this.toolbar;
49070     },
49071     
49072     setActiveState : function(active){
49073         this.active = active;
49074         if(!active){
49075             this.fireEvent("deactivate", this);
49076         }else{
49077             this.fireEvent("activate", this);
49078         }
49079     },
49080     /**
49081      * Updates this panel's element
49082      * @param {String} content The new content
49083      * @param {Boolean} loadScripts (optional) true to look for and process scripts
49084     */
49085     setContent : function(content, loadScripts){
49086         this.el.update(content, loadScripts);
49087     },
49088
49089     ignoreResize : function(w, h){
49090         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
49091             return true;
49092         }else{
49093             this.lastSize = {width: w, height: h};
49094             return false;
49095         }
49096     },
49097     /**
49098      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
49099      * @return {Roo.UpdateManager} The UpdateManager
49100      */
49101     getUpdateManager : function(){
49102         return this.el.getUpdateManager();
49103     },
49104      /**
49105      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
49106      * @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:
49107 <pre><code>
49108 panel.load({
49109     url: "your-url.php",
49110     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
49111     callback: yourFunction,
49112     scope: yourObject, //(optional scope)
49113     discardUrl: false,
49114     nocache: false,
49115     text: "Loading...",
49116     timeout: 30,
49117     scripts: false
49118 });
49119 </code></pre>
49120      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
49121      * 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.
49122      * @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}
49123      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
49124      * @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.
49125      * @return {Roo.ContentPanel} this
49126      */
49127     load : function(){
49128         var um = this.el.getUpdateManager();
49129         um.update.apply(um, arguments);
49130         return this;
49131     },
49132
49133
49134     /**
49135      * 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.
49136      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
49137      * @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)
49138      * @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)
49139      * @return {Roo.UpdateManager} The UpdateManager
49140      */
49141     setUrl : function(url, params, loadOnce){
49142         if(this.refreshDelegate){
49143             this.removeListener("activate", this.refreshDelegate);
49144         }
49145         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
49146         this.on("activate", this.refreshDelegate);
49147         return this.el.getUpdateManager();
49148     },
49149     
49150     _handleRefresh : function(url, params, loadOnce){
49151         if(!loadOnce || !this.loaded){
49152             var updater = this.el.getUpdateManager();
49153             updater.update(url, params, this._setLoaded.createDelegate(this));
49154         }
49155     },
49156     
49157     _setLoaded : function(){
49158         this.loaded = true;
49159     }, 
49160     
49161     /**
49162      * Returns this panel's id
49163      * @return {String} 
49164      */
49165     getId : function(){
49166         return this.el.id;
49167     },
49168     
49169     /** 
49170      * Returns this panel's element - used by regiosn to add.
49171      * @return {Roo.Element} 
49172      */
49173     getEl : function(){
49174         return this.wrapEl || this.el;
49175     },
49176     
49177     adjustForComponents : function(width, height)
49178     {
49179         //Roo.log('adjustForComponents ');
49180         if(this.resizeEl != this.el){
49181             width -= this.el.getFrameWidth('lr');
49182             height -= this.el.getFrameWidth('tb');
49183         }
49184         if(this.toolbar){
49185             var te = this.toolbar.getEl();
49186             height -= te.getHeight();
49187             te.setWidth(width);
49188         }
49189         if(this.footer){
49190             var te = this.footer.getEl();
49191             Roo.log("footer:" + te.getHeight());
49192             
49193             height -= te.getHeight();
49194             te.setWidth(width);
49195         }
49196         
49197         
49198         if(this.adjustments){
49199             width += this.adjustments[0];
49200             height += this.adjustments[1];
49201         }
49202         return {"width": width, "height": height};
49203     },
49204     
49205     setSize : function(width, height){
49206         if(this.fitToFrame && !this.ignoreResize(width, height)){
49207             if(this.fitContainer && this.resizeEl != this.el){
49208                 this.el.setSize(width, height);
49209             }
49210             var size = this.adjustForComponents(width, height);
49211             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
49212             this.fireEvent('resize', this, size.width, size.height);
49213         }
49214     },
49215     
49216     /**
49217      * Returns this panel's title
49218      * @return {String} 
49219      */
49220     getTitle : function(){
49221         return this.title;
49222     },
49223     
49224     /**
49225      * Set this panel's title
49226      * @param {String} title
49227      */
49228     setTitle : function(title){
49229         this.title = title;
49230         if(this.region){
49231             this.region.updatePanelTitle(this, title);
49232         }
49233     },
49234     
49235     /**
49236      * Returns true is this panel was configured to be closable
49237      * @return {Boolean} 
49238      */
49239     isClosable : function(){
49240         return this.closable;
49241     },
49242     
49243     beforeSlide : function(){
49244         this.el.clip();
49245         this.resizeEl.clip();
49246     },
49247     
49248     afterSlide : function(){
49249         this.el.unclip();
49250         this.resizeEl.unclip();
49251     },
49252     
49253     /**
49254      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
49255      *   Will fail silently if the {@link #setUrl} method has not been called.
49256      *   This does not activate the panel, just updates its content.
49257      */
49258     refresh : function(){
49259         if(this.refreshDelegate){
49260            this.loaded = false;
49261            this.refreshDelegate();
49262         }
49263     },
49264     
49265     /**
49266      * Destroys this panel
49267      */
49268     destroy : function(){
49269         this.el.removeAllListeners();
49270         var tempEl = document.createElement("span");
49271         tempEl.appendChild(this.el.dom);
49272         tempEl.innerHTML = "";
49273         this.el.remove();
49274         this.el = null;
49275     },
49276     
49277     /**
49278      * form - if the content panel contains a form - this is a reference to it.
49279      * @type {Roo.form.Form}
49280      */
49281     form : false,
49282     /**
49283      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
49284      *    This contains a reference to it.
49285      * @type {Roo.View}
49286      */
49287     view : false,
49288     
49289       /**
49290      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
49291      * <pre><code>
49292
49293 layout.addxtype({
49294        xtype : 'Form',
49295        items: [ .... ]
49296    }
49297 );
49298
49299 </code></pre>
49300      * @param {Object} cfg Xtype definition of item to add.
49301      */
49302     
49303     addxtype : function(cfg) {
49304         // add form..
49305         if (cfg.xtype.match(/^Form$/)) {
49306             
49307             var el;
49308             //if (this.footer) {
49309             //    el = this.footer.container.insertSibling(false, 'before');
49310             //} else {
49311                 el = this.el.createChild();
49312             //}
49313
49314             this.form = new  Roo.form.Form(cfg);
49315             
49316             
49317             if ( this.form.allItems.length) this.form.render(el.dom);
49318             return this.form;
49319         }
49320         // should only have one of theses..
49321         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
49322             // views.. should not be just added - used named prop 'view''
49323             
49324             cfg.el = this.el.appendChild(document.createElement("div"));
49325             // factory?
49326             
49327             var ret = new Roo.factory(cfg);
49328              
49329              ret.render && ret.render(false, ''); // render blank..
49330             this.view = ret;
49331             return ret;
49332         }
49333         return false;
49334     }
49335 });
49336
49337 /**
49338  * @class Roo.GridPanel
49339  * @extends Roo.ContentPanel
49340  * @constructor
49341  * Create a new GridPanel.
49342  * @param {Roo.grid.Grid} grid The grid for this panel
49343  * @param {String/Object} config A string to set only the panel's title, or a config object
49344  */
49345 Roo.GridPanel = function(grid, config){
49346     
49347   
49348     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
49349         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
49350         
49351     this.wrapper.dom.appendChild(grid.getGridEl().dom);
49352     
49353     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
49354     
49355     if(this.toolbar){
49356         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
49357     }
49358     // xtype created footer. - not sure if will work as we normally have to render first..
49359     if (this.footer && !this.footer.el && this.footer.xtype) {
49360         
49361         this.footer.container = this.grid.getView().getFooterPanel(true);
49362         this.footer.dataSource = this.grid.dataSource;
49363         this.footer = Roo.factory(this.footer, Roo);
49364         
49365     }
49366     
49367     grid.monitorWindowResize = false; // turn off autosizing
49368     grid.autoHeight = false;
49369     grid.autoWidth = false;
49370     this.grid = grid;
49371     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
49372 };
49373
49374 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
49375     getId : function(){
49376         return this.grid.id;
49377     },
49378     
49379     /**
49380      * Returns the grid for this panel
49381      * @return {Roo.grid.Grid} 
49382      */
49383     getGrid : function(){
49384         return this.grid;    
49385     },
49386     
49387     setSize : function(width, height){
49388         if(!this.ignoreResize(width, height)){
49389             var grid = this.grid;
49390             var size = this.adjustForComponents(width, height);
49391             grid.getGridEl().setSize(size.width, size.height);
49392             grid.autoSize();
49393         }
49394     },
49395     
49396     beforeSlide : function(){
49397         this.grid.getView().scroller.clip();
49398     },
49399     
49400     afterSlide : function(){
49401         this.grid.getView().scroller.unclip();
49402     },
49403     
49404     destroy : function(){
49405         this.grid.destroy();
49406         delete this.grid;
49407         Roo.GridPanel.superclass.destroy.call(this); 
49408     }
49409 });
49410
49411
49412 /**
49413  * @class Roo.NestedLayoutPanel
49414  * @extends Roo.ContentPanel
49415  * @constructor
49416  * Create a new NestedLayoutPanel.
49417  * 
49418  * 
49419  * @param {Roo.BorderLayout} layout The layout for this panel
49420  * @param {String/Object} config A string to set only the title or a config object
49421  */
49422 Roo.NestedLayoutPanel = function(layout, config)
49423 {
49424     // construct with only one argument..
49425     /* FIXME - implement nicer consturctors
49426     if (layout.layout) {
49427         config = layout;
49428         layout = config.layout;
49429         delete config.layout;
49430     }
49431     if (layout.xtype && !layout.getEl) {
49432         // then layout needs constructing..
49433         layout = Roo.factory(layout, Roo);
49434     }
49435     */
49436     
49437     
49438     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
49439     
49440     layout.monitorWindowResize = false; // turn off autosizing
49441     this.layout = layout;
49442     this.layout.getEl().addClass("x-layout-nested-layout");
49443     
49444     
49445     
49446     
49447 };
49448
49449 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
49450
49451     setSize : function(width, height){
49452         if(!this.ignoreResize(width, height)){
49453             var size = this.adjustForComponents(width, height);
49454             var el = this.layout.getEl();
49455             el.setSize(size.width, size.height);
49456             var touch = el.dom.offsetWidth;
49457             this.layout.layout();
49458             // ie requires a double layout on the first pass
49459             if(Roo.isIE && !this.initialized){
49460                 this.initialized = true;
49461                 this.layout.layout();
49462             }
49463         }
49464     },
49465     
49466     // activate all subpanels if not currently active..
49467     
49468     setActiveState : function(active){
49469         this.active = active;
49470         if(!active){
49471             this.fireEvent("deactivate", this);
49472             return;
49473         }
49474         
49475         this.fireEvent("activate", this);
49476         // not sure if this should happen before or after..
49477         if (!this.layout) {
49478             return; // should not happen..
49479         }
49480         var reg = false;
49481         for (var r in this.layout.regions) {
49482             reg = this.layout.getRegion(r);
49483             if (reg.getActivePanel()) {
49484                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
49485                 reg.setActivePanel(reg.getActivePanel());
49486                 continue;
49487             }
49488             if (!reg.panels.length) {
49489                 continue;
49490             }
49491             reg.showPanel(reg.getPanel(0));
49492         }
49493         
49494         
49495         
49496         
49497     },
49498     
49499     /**
49500      * Returns the nested BorderLayout for this panel
49501      * @return {Roo.BorderLayout} 
49502      */
49503     getLayout : function(){
49504         return this.layout;
49505     },
49506     
49507      /**
49508      * Adds a xtype elements to the layout of the nested panel
49509      * <pre><code>
49510
49511 panel.addxtype({
49512        xtype : 'ContentPanel',
49513        region: 'west',
49514        items: [ .... ]
49515    }
49516 );
49517
49518 panel.addxtype({
49519         xtype : 'NestedLayoutPanel',
49520         region: 'west',
49521         layout: {
49522            center: { },
49523            west: { }   
49524         },
49525         items : [ ... list of content panels or nested layout panels.. ]
49526    }
49527 );
49528 </code></pre>
49529      * @param {Object} cfg Xtype definition of item to add.
49530      */
49531     addxtype : function(cfg) {
49532         return this.layout.addxtype(cfg);
49533     
49534     }
49535 });
49536
49537 Roo.ScrollPanel = function(el, config, content){
49538     config = config || {};
49539     config.fitToFrame = true;
49540     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
49541     
49542     this.el.dom.style.overflow = "hidden";
49543     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
49544     this.el.removeClass("x-layout-inactive-content");
49545     this.el.on("mousewheel", this.onWheel, this);
49546
49547     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
49548     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
49549     up.unselectable(); down.unselectable();
49550     up.on("click", this.scrollUp, this);
49551     down.on("click", this.scrollDown, this);
49552     up.addClassOnOver("x-scroller-btn-over");
49553     down.addClassOnOver("x-scroller-btn-over");
49554     up.addClassOnClick("x-scroller-btn-click");
49555     down.addClassOnClick("x-scroller-btn-click");
49556     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
49557
49558     this.resizeEl = this.el;
49559     this.el = wrap; this.up = up; this.down = down;
49560 };
49561
49562 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
49563     increment : 100,
49564     wheelIncrement : 5,
49565     scrollUp : function(){
49566         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
49567     },
49568
49569     scrollDown : function(){
49570         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
49571     },
49572
49573     afterScroll : function(){
49574         var el = this.resizeEl;
49575         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
49576         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
49577         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
49578     },
49579
49580     setSize : function(){
49581         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
49582         this.afterScroll();
49583     },
49584
49585     onWheel : function(e){
49586         var d = e.getWheelDelta();
49587         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
49588         this.afterScroll();
49589         e.stopEvent();
49590     },
49591
49592     setContent : function(content, loadScripts){
49593         this.resizeEl.update(content, loadScripts);
49594     }
49595
49596 });
49597
49598
49599
49600
49601
49602
49603
49604
49605
49606 /**
49607  * @class Roo.TreePanel
49608  * @extends Roo.ContentPanel
49609  * @constructor
49610  * Create a new TreePanel. - defaults to fit/scoll contents.
49611  * @param {String/Object} config A string to set only the panel's title, or a config object
49612  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
49613  */
49614 Roo.TreePanel = function(config){
49615     var el = config.el;
49616     var tree = config.tree;
49617     delete config.tree; 
49618     delete config.el; // hopefull!
49619     
49620     // wrapper for IE7 strict & safari scroll issue
49621     
49622     var treeEl = el.createChild();
49623     config.resizeEl = treeEl;
49624     
49625     
49626     
49627     Roo.TreePanel.superclass.constructor.call(this, el, config);
49628  
49629  
49630     this.tree = new Roo.tree.TreePanel(treeEl , tree);
49631     //console.log(tree);
49632     this.on('activate', function()
49633     {
49634         if (this.tree.rendered) {
49635             return;
49636         }
49637         //console.log('render tree');
49638         this.tree.render();
49639     });
49640     // this should not be needed.. - it's actually the 'el' that resizes?
49641     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
49642     
49643     //this.on('resize',  function (cp, w, h) {
49644     //        this.tree.innerCt.setWidth(w);
49645     //        this.tree.innerCt.setHeight(h);
49646     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
49647     //});
49648
49649         
49650     
49651 };
49652
49653 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
49654     fitToFrame : true,
49655     autoScroll : true
49656 });
49657
49658
49659
49660
49661
49662
49663
49664
49665
49666
49667
49668 /*
49669  * Based on:
49670  * Ext JS Library 1.1.1
49671  * Copyright(c) 2006-2007, Ext JS, LLC.
49672  *
49673  * Originally Released Under LGPL - original licence link has changed is not relivant.
49674  *
49675  * Fork - LGPL
49676  * <script type="text/javascript">
49677  */
49678  
49679
49680 /**
49681  * @class Roo.ReaderLayout
49682  * @extends Roo.BorderLayout
49683  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
49684  * center region containing two nested regions (a top one for a list view and one for item preview below),
49685  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
49686  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
49687  * expedites the setup of the overall layout and regions for this common application style.
49688  * Example:
49689  <pre><code>
49690 var reader = new Roo.ReaderLayout();
49691 var CP = Roo.ContentPanel;  // shortcut for adding
49692
49693 reader.beginUpdate();
49694 reader.add("north", new CP("north", "North"));
49695 reader.add("west", new CP("west", {title: "West"}));
49696 reader.add("east", new CP("east", {title: "East"}));
49697
49698 reader.regions.listView.add(new CP("listView", "List"));
49699 reader.regions.preview.add(new CP("preview", "Preview"));
49700 reader.endUpdate();
49701 </code></pre>
49702 * @constructor
49703 * Create a new ReaderLayout
49704 * @param {Object} config Configuration options
49705 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
49706 * document.body if omitted)
49707 */
49708 Roo.ReaderLayout = function(config, renderTo){
49709     var c = config || {size:{}};
49710     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
49711         north: c.north !== false ? Roo.apply({
49712             split:false,
49713             initialSize: 32,
49714             titlebar: false
49715         }, c.north) : false,
49716         west: c.west !== false ? Roo.apply({
49717             split:true,
49718             initialSize: 200,
49719             minSize: 175,
49720             maxSize: 400,
49721             titlebar: true,
49722             collapsible: true,
49723             animate: true,
49724             margins:{left:5,right:0,bottom:5,top:5},
49725             cmargins:{left:5,right:5,bottom:5,top:5}
49726         }, c.west) : false,
49727         east: c.east !== false ? Roo.apply({
49728             split:true,
49729             initialSize: 200,
49730             minSize: 175,
49731             maxSize: 400,
49732             titlebar: true,
49733             collapsible: true,
49734             animate: true,
49735             margins:{left:0,right:5,bottom:5,top:5},
49736             cmargins:{left:5,right:5,bottom:5,top:5}
49737         }, c.east) : false,
49738         center: Roo.apply({
49739             tabPosition: 'top',
49740             autoScroll:false,
49741             closeOnTab: true,
49742             titlebar:false,
49743             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
49744         }, c.center)
49745     });
49746
49747     this.el.addClass('x-reader');
49748
49749     this.beginUpdate();
49750
49751     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
49752         south: c.preview !== false ? Roo.apply({
49753             split:true,
49754             initialSize: 200,
49755             minSize: 100,
49756             autoScroll:true,
49757             collapsible:true,
49758             titlebar: true,
49759             cmargins:{top:5,left:0, right:0, bottom:0}
49760         }, c.preview) : false,
49761         center: Roo.apply({
49762             autoScroll:false,
49763             titlebar:false,
49764             minHeight:200
49765         }, c.listView)
49766     });
49767     this.add('center', new Roo.NestedLayoutPanel(inner,
49768             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
49769
49770     this.endUpdate();
49771
49772     this.regions.preview = inner.getRegion('south');
49773     this.regions.listView = inner.getRegion('center');
49774 };
49775
49776 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
49777  * Based on:
49778  * Ext JS Library 1.1.1
49779  * Copyright(c) 2006-2007, Ext JS, LLC.
49780  *
49781  * Originally Released Under LGPL - original licence link has changed is not relivant.
49782  *
49783  * Fork - LGPL
49784  * <script type="text/javascript">
49785  */
49786  
49787 /**
49788  * @class Roo.grid.Grid
49789  * @extends Roo.util.Observable
49790  * This class represents the primary interface of a component based grid control.
49791  * <br><br>Usage:<pre><code>
49792  var grid = new Roo.grid.Grid("my-container-id", {
49793      ds: myDataStore,
49794      cm: myColModel,
49795      selModel: mySelectionModel,
49796      autoSizeColumns: true,
49797      monitorWindowResize: false,
49798      trackMouseOver: true
49799  });
49800  // set any options
49801  grid.render();
49802  * </code></pre>
49803  * <b>Common Problems:</b><br/>
49804  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
49805  * element will correct this<br/>
49806  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
49807  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
49808  * are unpredictable.<br/>
49809  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
49810  * grid to calculate dimensions/offsets.<br/>
49811   * @constructor
49812  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
49813  * The container MUST have some type of size defined for the grid to fill. The container will be
49814  * automatically set to position relative if it isn't already.
49815  * @param {Object} config A config object that sets properties on this grid.
49816  */
49817 Roo.grid.Grid = function(container, config){
49818         // initialize the container
49819         this.container = Roo.get(container);
49820         this.container.update("");
49821         this.container.setStyle("overflow", "hidden");
49822     this.container.addClass('x-grid-container');
49823
49824     this.id = this.container.id;
49825
49826     Roo.apply(this, config);
49827     // check and correct shorthanded configs
49828     if(this.ds){
49829         this.dataSource = this.ds;
49830         delete this.ds;
49831     }
49832     if(this.cm){
49833         this.colModel = this.cm;
49834         delete this.cm;
49835     }
49836     if(this.sm){
49837         this.selModel = this.sm;
49838         delete this.sm;
49839     }
49840
49841     if (this.selModel) {
49842         this.selModel = Roo.factory(this.selModel, Roo.grid);
49843         this.sm = this.selModel;
49844         this.sm.xmodule = this.xmodule || false;
49845     }
49846     if (typeof(this.colModel.config) == 'undefined') {
49847         this.colModel = new Roo.grid.ColumnModel(this.colModel);
49848         this.cm = this.colModel;
49849         this.cm.xmodule = this.xmodule || false;
49850     }
49851     if (this.dataSource) {
49852         this.dataSource= Roo.factory(this.dataSource, Roo.data);
49853         this.ds = this.dataSource;
49854         this.ds.xmodule = this.xmodule || false;
49855          
49856     }
49857     
49858     
49859     
49860     if(this.width){
49861         this.container.setWidth(this.width);
49862     }
49863
49864     if(this.height){
49865         this.container.setHeight(this.height);
49866     }
49867     /** @private */
49868         this.addEvents({
49869         // raw events
49870         /**
49871          * @event click
49872          * The raw click event for the entire grid.
49873          * @param {Roo.EventObject} e
49874          */
49875         "click" : true,
49876         /**
49877          * @event dblclick
49878          * The raw dblclick event for the entire grid.
49879          * @param {Roo.EventObject} e
49880          */
49881         "dblclick" : true,
49882         /**
49883          * @event contextmenu
49884          * The raw contextmenu event for the entire grid.
49885          * @param {Roo.EventObject} e
49886          */
49887         "contextmenu" : true,
49888         /**
49889          * @event mousedown
49890          * The raw mousedown event for the entire grid.
49891          * @param {Roo.EventObject} e
49892          */
49893         "mousedown" : true,
49894         /**
49895          * @event mouseup
49896          * The raw mouseup event for the entire grid.
49897          * @param {Roo.EventObject} e
49898          */
49899         "mouseup" : true,
49900         /**
49901          * @event mouseover
49902          * The raw mouseover event for the entire grid.
49903          * @param {Roo.EventObject} e
49904          */
49905         "mouseover" : true,
49906         /**
49907          * @event mouseout
49908          * The raw mouseout event for the entire grid.
49909          * @param {Roo.EventObject} e
49910          */
49911         "mouseout" : true,
49912         /**
49913          * @event keypress
49914          * The raw keypress event for the entire grid.
49915          * @param {Roo.EventObject} e
49916          */
49917         "keypress" : true,
49918         /**
49919          * @event keydown
49920          * The raw keydown event for the entire grid.
49921          * @param {Roo.EventObject} e
49922          */
49923         "keydown" : true,
49924
49925         // custom events
49926
49927         /**
49928          * @event cellclick
49929          * Fires when a cell is clicked
49930          * @param {Grid} this
49931          * @param {Number} rowIndex
49932          * @param {Number} columnIndex
49933          * @param {Roo.EventObject} e
49934          */
49935         "cellclick" : true,
49936         /**
49937          * @event celldblclick
49938          * Fires when a cell is double clicked
49939          * @param {Grid} this
49940          * @param {Number} rowIndex
49941          * @param {Number} columnIndex
49942          * @param {Roo.EventObject} e
49943          */
49944         "celldblclick" : true,
49945         /**
49946          * @event rowclick
49947          * Fires when a row is clicked
49948          * @param {Grid} this
49949          * @param {Number} rowIndex
49950          * @param {Roo.EventObject} e
49951          */
49952         "rowclick" : true,
49953         /**
49954          * @event rowdblclick
49955          * Fires when a row is double clicked
49956          * @param {Grid} this
49957          * @param {Number} rowIndex
49958          * @param {Roo.EventObject} e
49959          */
49960         "rowdblclick" : true,
49961         /**
49962          * @event headerclick
49963          * Fires when a header is clicked
49964          * @param {Grid} this
49965          * @param {Number} columnIndex
49966          * @param {Roo.EventObject} e
49967          */
49968         "headerclick" : true,
49969         /**
49970          * @event headerdblclick
49971          * Fires when a header cell is double clicked
49972          * @param {Grid} this
49973          * @param {Number} columnIndex
49974          * @param {Roo.EventObject} e
49975          */
49976         "headerdblclick" : true,
49977         /**
49978          * @event rowcontextmenu
49979          * Fires when a row is right clicked
49980          * @param {Grid} this
49981          * @param {Number} rowIndex
49982          * @param {Roo.EventObject} e
49983          */
49984         "rowcontextmenu" : true,
49985         /**
49986          * @event cellcontextmenu
49987          * Fires when a cell is right clicked
49988          * @param {Grid} this
49989          * @param {Number} rowIndex
49990          * @param {Number} cellIndex
49991          * @param {Roo.EventObject} e
49992          */
49993          "cellcontextmenu" : true,
49994         /**
49995          * @event headercontextmenu
49996          * Fires when a header is right clicked
49997          * @param {Grid} this
49998          * @param {Number} columnIndex
49999          * @param {Roo.EventObject} e
50000          */
50001         "headercontextmenu" : true,
50002         /**
50003          * @event bodyscroll
50004          * Fires when the body element is scrolled
50005          * @param {Number} scrollLeft
50006          * @param {Number} scrollTop
50007          */
50008         "bodyscroll" : true,
50009         /**
50010          * @event columnresize
50011          * Fires when the user resizes a column
50012          * @param {Number} columnIndex
50013          * @param {Number} newSize
50014          */
50015         "columnresize" : true,
50016         /**
50017          * @event columnmove
50018          * Fires when the user moves a column
50019          * @param {Number} oldIndex
50020          * @param {Number} newIndex
50021          */
50022         "columnmove" : true,
50023         /**
50024          * @event startdrag
50025          * Fires when row(s) start being dragged
50026          * @param {Grid} this
50027          * @param {Roo.GridDD} dd The drag drop object
50028          * @param {event} e The raw browser event
50029          */
50030         "startdrag" : true,
50031         /**
50032          * @event enddrag
50033          * Fires when a drag operation is complete
50034          * @param {Grid} this
50035          * @param {Roo.GridDD} dd The drag drop object
50036          * @param {event} e The raw browser event
50037          */
50038         "enddrag" : true,
50039         /**
50040          * @event dragdrop
50041          * Fires when dragged row(s) are dropped on a valid DD target
50042          * @param {Grid} this
50043          * @param {Roo.GridDD} dd The drag drop object
50044          * @param {String} targetId The target drag drop object
50045          * @param {event} e The raw browser event
50046          */
50047         "dragdrop" : true,
50048         /**
50049          * @event dragover
50050          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
50051          * @param {Grid} this
50052          * @param {Roo.GridDD} dd The drag drop object
50053          * @param {String} targetId The target drag drop object
50054          * @param {event} e The raw browser event
50055          */
50056         "dragover" : true,
50057         /**
50058          * @event dragenter
50059          *  Fires when the dragged row(s) first cross another DD target while being dragged
50060          * @param {Grid} this
50061          * @param {Roo.GridDD} dd The drag drop object
50062          * @param {String} targetId The target drag drop object
50063          * @param {event} e The raw browser event
50064          */
50065         "dragenter" : true,
50066         /**
50067          * @event dragout
50068          * Fires when the dragged row(s) leave another DD target while being dragged
50069          * @param {Grid} this
50070          * @param {Roo.GridDD} dd The drag drop object
50071          * @param {String} targetId The target drag drop object
50072          * @param {event} e The raw browser event
50073          */
50074         "dragout" : true,
50075         /**
50076          * @event rowclass
50077          * Fires when a row is rendered, so you can change add a style to it.
50078          * @param {GridView} gridview   The grid view
50079          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
50080          */
50081         'rowclass' : true,
50082
50083         /**
50084          * @event render
50085          * Fires when the grid is rendered
50086          * @param {Grid} grid
50087          */
50088         'render' : true
50089     });
50090
50091     Roo.grid.Grid.superclass.constructor.call(this);
50092 };
50093 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
50094     
50095     /**
50096      * @cfg {String} ddGroup - drag drop group.
50097      */
50098
50099     /**
50100      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
50101      */
50102     minColumnWidth : 25,
50103
50104     /**
50105      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
50106      * <b>on initial render.</b> It is more efficient to explicitly size the columns
50107      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
50108      */
50109     autoSizeColumns : false,
50110
50111     /**
50112      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
50113      */
50114     autoSizeHeaders : true,
50115
50116     /**
50117      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
50118      */
50119     monitorWindowResize : true,
50120
50121     /**
50122      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
50123      * rows measured to get a columns size. Default is 0 (all rows).
50124      */
50125     maxRowsToMeasure : 0,
50126
50127     /**
50128      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
50129      */
50130     trackMouseOver : true,
50131
50132     /**
50133     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
50134     */
50135     
50136     /**
50137     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
50138     */
50139     enableDragDrop : false,
50140     
50141     /**
50142     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
50143     */
50144     enableColumnMove : true,
50145     
50146     /**
50147     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
50148     */
50149     enableColumnHide : true,
50150     
50151     /**
50152     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
50153     */
50154     enableRowHeightSync : false,
50155     
50156     /**
50157     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
50158     */
50159     stripeRows : true,
50160     
50161     /**
50162     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
50163     */
50164     autoHeight : false,
50165
50166     /**
50167      * @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.
50168      */
50169     autoExpandColumn : false,
50170
50171     /**
50172     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
50173     * Default is 50.
50174     */
50175     autoExpandMin : 50,
50176
50177     /**
50178     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
50179     */
50180     autoExpandMax : 1000,
50181
50182     /**
50183     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
50184     */
50185     view : null,
50186
50187     /**
50188     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
50189     */
50190     loadMask : false,
50191     /**
50192     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
50193     */
50194     dropTarget: false,
50195     
50196    
50197     
50198     // private
50199     rendered : false,
50200
50201     /**
50202     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
50203     * of a fixed width. Default is false.
50204     */
50205     /**
50206     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
50207     */
50208     /**
50209      * Called once after all setup has been completed and the grid is ready to be rendered.
50210      * @return {Roo.grid.Grid} this
50211      */
50212     render : function()
50213     {
50214         var c = this.container;
50215         // try to detect autoHeight/width mode
50216         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
50217             this.autoHeight = true;
50218         }
50219         var view = this.getView();
50220         view.init(this);
50221
50222         c.on("click", this.onClick, this);
50223         c.on("dblclick", this.onDblClick, this);
50224         c.on("contextmenu", this.onContextMenu, this);
50225         c.on("keydown", this.onKeyDown, this);
50226
50227         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
50228
50229         this.getSelectionModel().init(this);
50230
50231         view.render();
50232
50233         if(this.loadMask){
50234             this.loadMask = new Roo.LoadMask(this.container,
50235                     Roo.apply({store:this.dataSource}, this.loadMask));
50236         }
50237         
50238         
50239         if (this.toolbar && this.toolbar.xtype) {
50240             this.toolbar.container = this.getView().getHeaderPanel(true);
50241             this.toolbar = new Roo.Toolbar(this.toolbar);
50242         }
50243         if (this.footer && this.footer.xtype) {
50244             this.footer.dataSource = this.getDataSource();
50245             this.footer.container = this.getView().getFooterPanel(true);
50246             this.footer = Roo.factory(this.footer, Roo);
50247         }
50248         if (this.dropTarget && this.dropTarget.xtype) {
50249             delete this.dropTarget.xtype;
50250             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
50251         }
50252         
50253         
50254         this.rendered = true;
50255         this.fireEvent('render', this);
50256         return this;
50257     },
50258
50259         /**
50260          * Reconfigures the grid to use a different Store and Column Model.
50261          * The View will be bound to the new objects and refreshed.
50262          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
50263          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
50264          */
50265     reconfigure : function(dataSource, colModel){
50266         if(this.loadMask){
50267             this.loadMask.destroy();
50268             this.loadMask = new Roo.LoadMask(this.container,
50269                     Roo.apply({store:dataSource}, this.loadMask));
50270         }
50271         this.view.bind(dataSource, colModel);
50272         this.dataSource = dataSource;
50273         this.colModel = colModel;
50274         this.view.refresh(true);
50275     },
50276
50277     // private
50278     onKeyDown : function(e){
50279         this.fireEvent("keydown", e);
50280     },
50281
50282     /**
50283      * Destroy this grid.
50284      * @param {Boolean} removeEl True to remove the element
50285      */
50286     destroy : function(removeEl, keepListeners){
50287         if(this.loadMask){
50288             this.loadMask.destroy();
50289         }
50290         var c = this.container;
50291         c.removeAllListeners();
50292         this.view.destroy();
50293         this.colModel.purgeListeners();
50294         if(!keepListeners){
50295             this.purgeListeners();
50296         }
50297         c.update("");
50298         if(removeEl === true){
50299             c.remove();
50300         }
50301     },
50302
50303     // private
50304     processEvent : function(name, e){
50305         this.fireEvent(name, e);
50306         var t = e.getTarget();
50307         var v = this.view;
50308         var header = v.findHeaderIndex(t);
50309         if(header !== false){
50310             this.fireEvent("header" + name, this, header, e);
50311         }else{
50312             var row = v.findRowIndex(t);
50313             var cell = v.findCellIndex(t);
50314             if(row !== false){
50315                 this.fireEvent("row" + name, this, row, e);
50316                 if(cell !== false){
50317                     this.fireEvent("cell" + name, this, row, cell, e);
50318                 }
50319             }
50320         }
50321     },
50322
50323     // private
50324     onClick : function(e){
50325         this.processEvent("click", e);
50326     },
50327
50328     // private
50329     onContextMenu : function(e, t){
50330         this.processEvent("contextmenu", e);
50331     },
50332
50333     // private
50334     onDblClick : function(e){
50335         this.processEvent("dblclick", e);
50336     },
50337
50338     // private
50339     walkCells : function(row, col, step, fn, scope){
50340         var cm = this.colModel, clen = cm.getColumnCount();
50341         var ds = this.dataSource, rlen = ds.getCount(), first = true;
50342         if(step < 0){
50343             if(col < 0){
50344                 row--;
50345                 first = false;
50346             }
50347             while(row >= 0){
50348                 if(!first){
50349                     col = clen-1;
50350                 }
50351                 first = false;
50352                 while(col >= 0){
50353                     if(fn.call(scope || this, row, col, cm) === true){
50354                         return [row, col];
50355                     }
50356                     col--;
50357                 }
50358                 row--;
50359             }
50360         } else {
50361             if(col >= clen){
50362                 row++;
50363                 first = false;
50364             }
50365             while(row < rlen){
50366                 if(!first){
50367                     col = 0;
50368                 }
50369                 first = false;
50370                 while(col < clen){
50371                     if(fn.call(scope || this, row, col, cm) === true){
50372                         return [row, col];
50373                     }
50374                     col++;
50375                 }
50376                 row++;
50377             }
50378         }
50379         return null;
50380     },
50381
50382     // private
50383     getSelections : function(){
50384         return this.selModel.getSelections();
50385     },
50386
50387     /**
50388      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
50389      * but if manual update is required this method will initiate it.
50390      */
50391     autoSize : function(){
50392         if(this.rendered){
50393             this.view.layout();
50394             if(this.view.adjustForScroll){
50395                 this.view.adjustForScroll();
50396             }
50397         }
50398     },
50399
50400     /**
50401      * Returns the grid's underlying element.
50402      * @return {Element} The element
50403      */
50404     getGridEl : function(){
50405         return this.container;
50406     },
50407
50408     // private for compatibility, overridden by editor grid
50409     stopEditing : function(){},
50410
50411     /**
50412      * Returns the grid's SelectionModel.
50413      * @return {SelectionModel}
50414      */
50415     getSelectionModel : function(){
50416         if(!this.selModel){
50417             this.selModel = new Roo.grid.RowSelectionModel();
50418         }
50419         return this.selModel;
50420     },
50421
50422     /**
50423      * Returns the grid's DataSource.
50424      * @return {DataSource}
50425      */
50426     getDataSource : function(){
50427         return this.dataSource;
50428     },
50429
50430     /**
50431      * Returns the grid's ColumnModel.
50432      * @return {ColumnModel}
50433      */
50434     getColumnModel : function(){
50435         return this.colModel;
50436     },
50437
50438     /**
50439      * Returns the grid's GridView object.
50440      * @return {GridView}
50441      */
50442     getView : function(){
50443         if(!this.view){
50444             this.view = new Roo.grid.GridView(this.viewConfig);
50445         }
50446         return this.view;
50447     },
50448     /**
50449      * Called to get grid's drag proxy text, by default returns this.ddText.
50450      * @return {String}
50451      */
50452     getDragDropText : function(){
50453         var count = this.selModel.getCount();
50454         return String.format(this.ddText, count, count == 1 ? '' : 's');
50455     }
50456 });
50457 /**
50458  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
50459  * %0 is replaced with the number of selected rows.
50460  * @type String
50461  */
50462 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
50463  * Based on:
50464  * Ext JS Library 1.1.1
50465  * Copyright(c) 2006-2007, Ext JS, LLC.
50466  *
50467  * Originally Released Under LGPL - original licence link has changed is not relivant.
50468  *
50469  * Fork - LGPL
50470  * <script type="text/javascript">
50471  */
50472  
50473 Roo.grid.AbstractGridView = function(){
50474         this.grid = null;
50475         
50476         this.events = {
50477             "beforerowremoved" : true,
50478             "beforerowsinserted" : true,
50479             "beforerefresh" : true,
50480             "rowremoved" : true,
50481             "rowsinserted" : true,
50482             "rowupdated" : true,
50483             "refresh" : true
50484         };
50485     Roo.grid.AbstractGridView.superclass.constructor.call(this);
50486 };
50487
50488 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
50489     rowClass : "x-grid-row",
50490     cellClass : "x-grid-cell",
50491     tdClass : "x-grid-td",
50492     hdClass : "x-grid-hd",
50493     splitClass : "x-grid-hd-split",
50494     
50495         init: function(grid){
50496         this.grid = grid;
50497                 var cid = this.grid.getGridEl().id;
50498         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
50499         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
50500         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
50501         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
50502         },
50503         
50504         getColumnRenderers : function(){
50505         var renderers = [];
50506         var cm = this.grid.colModel;
50507         var colCount = cm.getColumnCount();
50508         for(var i = 0; i < colCount; i++){
50509             renderers[i] = cm.getRenderer(i);
50510         }
50511         return renderers;
50512     },
50513     
50514     getColumnIds : function(){
50515         var ids = [];
50516         var cm = this.grid.colModel;
50517         var colCount = cm.getColumnCount();
50518         for(var i = 0; i < colCount; i++){
50519             ids[i] = cm.getColumnId(i);
50520         }
50521         return ids;
50522     },
50523     
50524     getDataIndexes : function(){
50525         if(!this.indexMap){
50526             this.indexMap = this.buildIndexMap();
50527         }
50528         return this.indexMap.colToData;
50529     },
50530     
50531     getColumnIndexByDataIndex : function(dataIndex){
50532         if(!this.indexMap){
50533             this.indexMap = this.buildIndexMap();
50534         }
50535         return this.indexMap.dataToCol[dataIndex];
50536     },
50537     
50538     /**
50539      * Set a css style for a column dynamically. 
50540      * @param {Number} colIndex The index of the column
50541      * @param {String} name The css property name
50542      * @param {String} value The css value
50543      */
50544     setCSSStyle : function(colIndex, name, value){
50545         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
50546         Roo.util.CSS.updateRule(selector, name, value);
50547     },
50548     
50549     generateRules : function(cm){
50550         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
50551         Roo.util.CSS.removeStyleSheet(rulesId);
50552         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50553             var cid = cm.getColumnId(i);
50554             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
50555                          this.tdSelector, cid, " {\n}\n",
50556                          this.hdSelector, cid, " {\n}\n",
50557                          this.splitSelector, cid, " {\n}\n");
50558         }
50559         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
50560     }
50561 });/*
50562  * Based on:
50563  * Ext JS Library 1.1.1
50564  * Copyright(c) 2006-2007, Ext JS, LLC.
50565  *
50566  * Originally Released Under LGPL - original licence link has changed is not relivant.
50567  *
50568  * Fork - LGPL
50569  * <script type="text/javascript">
50570  */
50571
50572 // private
50573 // This is a support class used internally by the Grid components
50574 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
50575     this.grid = grid;
50576     this.view = grid.getView();
50577     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
50578     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
50579     if(hd2){
50580         this.setHandleElId(Roo.id(hd));
50581         this.setOuterHandleElId(Roo.id(hd2));
50582     }
50583     this.scroll = false;
50584 };
50585 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
50586     maxDragWidth: 120,
50587     getDragData : function(e){
50588         var t = Roo.lib.Event.getTarget(e);
50589         var h = this.view.findHeaderCell(t);
50590         if(h){
50591             return {ddel: h.firstChild, header:h};
50592         }
50593         return false;
50594     },
50595
50596     onInitDrag : function(e){
50597         this.view.headersDisabled = true;
50598         var clone = this.dragData.ddel.cloneNode(true);
50599         clone.id = Roo.id();
50600         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
50601         this.proxy.update(clone);
50602         return true;
50603     },
50604
50605     afterValidDrop : function(){
50606         var v = this.view;
50607         setTimeout(function(){
50608             v.headersDisabled = false;
50609         }, 50);
50610     },
50611
50612     afterInvalidDrop : function(){
50613         var v = this.view;
50614         setTimeout(function(){
50615             v.headersDisabled = false;
50616         }, 50);
50617     }
50618 });
50619 /*
50620  * Based on:
50621  * Ext JS Library 1.1.1
50622  * Copyright(c) 2006-2007, Ext JS, LLC.
50623  *
50624  * Originally Released Under LGPL - original licence link has changed is not relivant.
50625  *
50626  * Fork - LGPL
50627  * <script type="text/javascript">
50628  */
50629 // private
50630 // This is a support class used internally by the Grid components
50631 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
50632     this.grid = grid;
50633     this.view = grid.getView();
50634     // split the proxies so they don't interfere with mouse events
50635     this.proxyTop = Roo.DomHelper.append(document.body, {
50636         cls:"col-move-top", html:"&#160;"
50637     }, true);
50638     this.proxyBottom = Roo.DomHelper.append(document.body, {
50639         cls:"col-move-bottom", html:"&#160;"
50640     }, true);
50641     this.proxyTop.hide = this.proxyBottom.hide = function(){
50642         this.setLeftTop(-100,-100);
50643         this.setStyle("visibility", "hidden");
50644     };
50645     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
50646     // temporarily disabled
50647     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
50648     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
50649 };
50650 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
50651     proxyOffsets : [-4, -9],
50652     fly: Roo.Element.fly,
50653
50654     getTargetFromEvent : function(e){
50655         var t = Roo.lib.Event.getTarget(e);
50656         var cindex = this.view.findCellIndex(t);
50657         if(cindex !== false){
50658             return this.view.getHeaderCell(cindex);
50659         }
50660         return null;
50661     },
50662
50663     nextVisible : function(h){
50664         var v = this.view, cm = this.grid.colModel;
50665         h = h.nextSibling;
50666         while(h){
50667             if(!cm.isHidden(v.getCellIndex(h))){
50668                 return h;
50669             }
50670             h = h.nextSibling;
50671         }
50672         return null;
50673     },
50674
50675     prevVisible : function(h){
50676         var v = this.view, cm = this.grid.colModel;
50677         h = h.prevSibling;
50678         while(h){
50679             if(!cm.isHidden(v.getCellIndex(h))){
50680                 return h;
50681             }
50682             h = h.prevSibling;
50683         }
50684         return null;
50685     },
50686
50687     positionIndicator : function(h, n, e){
50688         var x = Roo.lib.Event.getPageX(e);
50689         var r = Roo.lib.Dom.getRegion(n.firstChild);
50690         var px, pt, py = r.top + this.proxyOffsets[1];
50691         if((r.right - x) <= (r.right-r.left)/2){
50692             px = r.right+this.view.borderWidth;
50693             pt = "after";
50694         }else{
50695             px = r.left;
50696             pt = "before";
50697         }
50698         var oldIndex = this.view.getCellIndex(h);
50699         var newIndex = this.view.getCellIndex(n);
50700
50701         if(this.grid.colModel.isFixed(newIndex)){
50702             return false;
50703         }
50704
50705         var locked = this.grid.colModel.isLocked(newIndex);
50706
50707         if(pt == "after"){
50708             newIndex++;
50709         }
50710         if(oldIndex < newIndex){
50711             newIndex--;
50712         }
50713         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
50714             return false;
50715         }
50716         px +=  this.proxyOffsets[0];
50717         this.proxyTop.setLeftTop(px, py);
50718         this.proxyTop.show();
50719         if(!this.bottomOffset){
50720             this.bottomOffset = this.view.mainHd.getHeight();
50721         }
50722         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
50723         this.proxyBottom.show();
50724         return pt;
50725     },
50726
50727     onNodeEnter : function(n, dd, e, data){
50728         if(data.header != n){
50729             this.positionIndicator(data.header, n, e);
50730         }
50731     },
50732
50733     onNodeOver : function(n, dd, e, data){
50734         var result = false;
50735         if(data.header != n){
50736             result = this.positionIndicator(data.header, n, e);
50737         }
50738         if(!result){
50739             this.proxyTop.hide();
50740             this.proxyBottom.hide();
50741         }
50742         return result ? this.dropAllowed : this.dropNotAllowed;
50743     },
50744
50745     onNodeOut : function(n, dd, e, data){
50746         this.proxyTop.hide();
50747         this.proxyBottom.hide();
50748     },
50749
50750     onNodeDrop : function(n, dd, e, data){
50751         var h = data.header;
50752         if(h != n){
50753             var cm = this.grid.colModel;
50754             var x = Roo.lib.Event.getPageX(e);
50755             var r = Roo.lib.Dom.getRegion(n.firstChild);
50756             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
50757             var oldIndex = this.view.getCellIndex(h);
50758             var newIndex = this.view.getCellIndex(n);
50759             var locked = cm.isLocked(newIndex);
50760             if(pt == "after"){
50761                 newIndex++;
50762             }
50763             if(oldIndex < newIndex){
50764                 newIndex--;
50765             }
50766             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
50767                 return false;
50768             }
50769             cm.setLocked(oldIndex, locked, true);
50770             cm.moveColumn(oldIndex, newIndex);
50771             this.grid.fireEvent("columnmove", oldIndex, newIndex);
50772             return true;
50773         }
50774         return false;
50775     }
50776 });
50777 /*
50778  * Based on:
50779  * Ext JS Library 1.1.1
50780  * Copyright(c) 2006-2007, Ext JS, LLC.
50781  *
50782  * Originally Released Under LGPL - original licence link has changed is not relivant.
50783  *
50784  * Fork - LGPL
50785  * <script type="text/javascript">
50786  */
50787   
50788 /**
50789  * @class Roo.grid.GridView
50790  * @extends Roo.util.Observable
50791  *
50792  * @constructor
50793  * @param {Object} config
50794  */
50795 Roo.grid.GridView = function(config){
50796     Roo.grid.GridView.superclass.constructor.call(this);
50797     this.el = null;
50798
50799     Roo.apply(this, config);
50800 };
50801
50802 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
50803
50804     unselectable :  'unselectable="on"',
50805     unselectableCls :  'x-unselectable',
50806     
50807     
50808     rowClass : "x-grid-row",
50809
50810     cellClass : "x-grid-col",
50811
50812     tdClass : "x-grid-td",
50813
50814     hdClass : "x-grid-hd",
50815
50816     splitClass : "x-grid-split",
50817
50818     sortClasses : ["sort-asc", "sort-desc"],
50819
50820     enableMoveAnim : false,
50821
50822     hlColor: "C3DAF9",
50823
50824     dh : Roo.DomHelper,
50825
50826     fly : Roo.Element.fly,
50827
50828     css : Roo.util.CSS,
50829
50830     borderWidth: 1,
50831
50832     splitOffset: 3,
50833
50834     scrollIncrement : 22,
50835
50836     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
50837
50838     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
50839
50840     bind : function(ds, cm){
50841         if(this.ds){
50842             this.ds.un("load", this.onLoad, this);
50843             this.ds.un("datachanged", this.onDataChange, this);
50844             this.ds.un("add", this.onAdd, this);
50845             this.ds.un("remove", this.onRemove, this);
50846             this.ds.un("update", this.onUpdate, this);
50847             this.ds.un("clear", this.onClear, this);
50848         }
50849         if(ds){
50850             ds.on("load", this.onLoad, this);
50851             ds.on("datachanged", this.onDataChange, this);
50852             ds.on("add", this.onAdd, this);
50853             ds.on("remove", this.onRemove, this);
50854             ds.on("update", this.onUpdate, this);
50855             ds.on("clear", this.onClear, this);
50856         }
50857         this.ds = ds;
50858
50859         if(this.cm){
50860             this.cm.un("widthchange", this.onColWidthChange, this);
50861             this.cm.un("headerchange", this.onHeaderChange, this);
50862             this.cm.un("hiddenchange", this.onHiddenChange, this);
50863             this.cm.un("columnmoved", this.onColumnMove, this);
50864             this.cm.un("columnlockchange", this.onColumnLock, this);
50865         }
50866         if(cm){
50867             this.generateRules(cm);
50868             cm.on("widthchange", this.onColWidthChange, this);
50869             cm.on("headerchange", this.onHeaderChange, this);
50870             cm.on("hiddenchange", this.onHiddenChange, this);
50871             cm.on("columnmoved", this.onColumnMove, this);
50872             cm.on("columnlockchange", this.onColumnLock, this);
50873         }
50874         this.cm = cm;
50875     },
50876
50877     init: function(grid){
50878         Roo.grid.GridView.superclass.init.call(this, grid);
50879
50880         this.bind(grid.dataSource, grid.colModel);
50881
50882         grid.on("headerclick", this.handleHeaderClick, this);
50883
50884         if(grid.trackMouseOver){
50885             grid.on("mouseover", this.onRowOver, this);
50886             grid.on("mouseout", this.onRowOut, this);
50887         }
50888         grid.cancelTextSelection = function(){};
50889         this.gridId = grid.id;
50890
50891         var tpls = this.templates || {};
50892
50893         if(!tpls.master){
50894             tpls.master = new Roo.Template(
50895                '<div class="x-grid" hidefocus="true">',
50896                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
50897                   '<div class="x-grid-topbar"></div>',
50898                   '<div class="x-grid-scroller"><div></div></div>',
50899                   '<div class="x-grid-locked">',
50900                       '<div class="x-grid-header">{lockedHeader}</div>',
50901                       '<div class="x-grid-body">{lockedBody}</div>',
50902                   "</div>",
50903                   '<div class="x-grid-viewport">',
50904                       '<div class="x-grid-header">{header}</div>',
50905                       '<div class="x-grid-body">{body}</div>',
50906                   "</div>",
50907                   '<div class="x-grid-bottombar"></div>',
50908                  
50909                   '<div class="x-grid-resize-proxy">&#160;</div>',
50910                "</div>"
50911             );
50912             tpls.master.disableformats = true;
50913         }
50914
50915         if(!tpls.header){
50916             tpls.header = new Roo.Template(
50917                '<table border="0" cellspacing="0" cellpadding="0">',
50918                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
50919                "</table>{splits}"
50920             );
50921             tpls.header.disableformats = true;
50922         }
50923         tpls.header.compile();
50924
50925         if(!tpls.hcell){
50926             tpls.hcell = new Roo.Template(
50927                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
50928                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
50929                 "</div></td>"
50930              );
50931              tpls.hcell.disableFormats = true;
50932         }
50933         tpls.hcell.compile();
50934
50935         if(!tpls.hsplit){
50936             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
50937                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
50938             tpls.hsplit.disableFormats = true;
50939         }
50940         tpls.hsplit.compile();
50941
50942         if(!tpls.body){
50943             tpls.body = new Roo.Template(
50944                '<table border="0" cellspacing="0" cellpadding="0">',
50945                "<tbody>{rows}</tbody>",
50946                "</table>"
50947             );
50948             tpls.body.disableFormats = true;
50949         }
50950         tpls.body.compile();
50951
50952         if(!tpls.row){
50953             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
50954             tpls.row.disableFormats = true;
50955         }
50956         tpls.row.compile();
50957
50958         if(!tpls.cell){
50959             tpls.cell = new Roo.Template(
50960                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
50961                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
50962                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
50963                 "</td>"
50964             );
50965             tpls.cell.disableFormats = true;
50966         }
50967         tpls.cell.compile();
50968
50969         this.templates = tpls;
50970     },
50971
50972     // remap these for backwards compat
50973     onColWidthChange : function(){
50974         this.updateColumns.apply(this, arguments);
50975     },
50976     onHeaderChange : function(){
50977         this.updateHeaders.apply(this, arguments);
50978     }, 
50979     onHiddenChange : function(){
50980         this.handleHiddenChange.apply(this, arguments);
50981     },
50982     onColumnMove : function(){
50983         this.handleColumnMove.apply(this, arguments);
50984     },
50985     onColumnLock : function(){
50986         this.handleLockChange.apply(this, arguments);
50987     },
50988
50989     onDataChange : function(){
50990         this.refresh();
50991         this.updateHeaderSortState();
50992     },
50993
50994     onClear : function(){
50995         this.refresh();
50996     },
50997
50998     onUpdate : function(ds, record){
50999         this.refreshRow(record);
51000     },
51001
51002     refreshRow : function(record){
51003         var ds = this.ds, index;
51004         if(typeof record == 'number'){
51005             index = record;
51006             record = ds.getAt(index);
51007         }else{
51008             index = ds.indexOf(record);
51009         }
51010         this.insertRows(ds, index, index, true);
51011         this.onRemove(ds, record, index+1, true);
51012         this.syncRowHeights(index, index);
51013         this.layout();
51014         this.fireEvent("rowupdated", this, index, record);
51015     },
51016
51017     onAdd : function(ds, records, index){
51018         this.insertRows(ds, index, index + (records.length-1));
51019     },
51020
51021     onRemove : function(ds, record, index, isUpdate){
51022         if(isUpdate !== true){
51023             this.fireEvent("beforerowremoved", this, index, record);
51024         }
51025         var bt = this.getBodyTable(), lt = this.getLockedTable();
51026         if(bt.rows[index]){
51027             bt.firstChild.removeChild(bt.rows[index]);
51028         }
51029         if(lt.rows[index]){
51030             lt.firstChild.removeChild(lt.rows[index]);
51031         }
51032         if(isUpdate !== true){
51033             this.stripeRows(index);
51034             this.syncRowHeights(index, index);
51035             this.layout();
51036             this.fireEvent("rowremoved", this, index, record);
51037         }
51038     },
51039
51040     onLoad : function(){
51041         this.scrollToTop();
51042     },
51043
51044     /**
51045      * Scrolls the grid to the top
51046      */
51047     scrollToTop : function(){
51048         if(this.scroller){
51049             this.scroller.dom.scrollTop = 0;
51050             this.syncScroll();
51051         }
51052     },
51053
51054     /**
51055      * Gets a panel in the header of the grid that can be used for toolbars etc.
51056      * After modifying the contents of this panel a call to grid.autoSize() may be
51057      * required to register any changes in size.
51058      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
51059      * @return Roo.Element
51060      */
51061     getHeaderPanel : function(doShow){
51062         if(doShow){
51063             this.headerPanel.show();
51064         }
51065         return this.headerPanel;
51066     },
51067
51068     /**
51069      * Gets a panel in the footer of the grid that can be used for toolbars etc.
51070      * After modifying the contents of this panel a call to grid.autoSize() may be
51071      * required to register any changes in size.
51072      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
51073      * @return Roo.Element
51074      */
51075     getFooterPanel : function(doShow){
51076         if(doShow){
51077             this.footerPanel.show();
51078         }
51079         return this.footerPanel;
51080     },
51081
51082     initElements : function(){
51083         var E = Roo.Element;
51084         var el = this.grid.getGridEl().dom.firstChild;
51085         var cs = el.childNodes;
51086
51087         this.el = new E(el);
51088         
51089          this.focusEl = new E(el.firstChild);
51090         this.focusEl.swallowEvent("click", true);
51091         
51092         this.headerPanel = new E(cs[1]);
51093         this.headerPanel.enableDisplayMode("block");
51094
51095         this.scroller = new E(cs[2]);
51096         this.scrollSizer = new E(this.scroller.dom.firstChild);
51097
51098         this.lockedWrap = new E(cs[3]);
51099         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
51100         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
51101
51102         this.mainWrap = new E(cs[4]);
51103         this.mainHd = new E(this.mainWrap.dom.firstChild);
51104         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
51105
51106         this.footerPanel = new E(cs[5]);
51107         this.footerPanel.enableDisplayMode("block");
51108
51109         this.resizeProxy = new E(cs[6]);
51110
51111         this.headerSelector = String.format(
51112            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
51113            this.lockedHd.id, this.mainHd.id
51114         );
51115
51116         this.splitterSelector = String.format(
51117            '#{0} div.x-grid-split, #{1} div.x-grid-split',
51118            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
51119         );
51120     },
51121     idToCssName : function(s)
51122     {
51123         return s.replace(/[^a-z0-9]+/ig, '-');
51124     },
51125
51126     getHeaderCell : function(index){
51127         return Roo.DomQuery.select(this.headerSelector)[index];
51128     },
51129
51130     getHeaderCellMeasure : function(index){
51131         return this.getHeaderCell(index).firstChild;
51132     },
51133
51134     getHeaderCellText : function(index){
51135         return this.getHeaderCell(index).firstChild.firstChild;
51136     },
51137
51138     getLockedTable : function(){
51139         return this.lockedBody.dom.firstChild;
51140     },
51141
51142     getBodyTable : function(){
51143         return this.mainBody.dom.firstChild;
51144     },
51145
51146     getLockedRow : function(index){
51147         return this.getLockedTable().rows[index];
51148     },
51149
51150     getRow : function(index){
51151         return this.getBodyTable().rows[index];
51152     },
51153
51154     getRowComposite : function(index){
51155         if(!this.rowEl){
51156             this.rowEl = new Roo.CompositeElementLite();
51157         }
51158         var els = [], lrow, mrow;
51159         if(lrow = this.getLockedRow(index)){
51160             els.push(lrow);
51161         }
51162         if(mrow = this.getRow(index)){
51163             els.push(mrow);
51164         }
51165         this.rowEl.elements = els;
51166         return this.rowEl;
51167     },
51168     /**
51169      * Gets the 'td' of the cell
51170      * 
51171      * @param {Integer} rowIndex row to select
51172      * @param {Integer} colIndex column to select
51173      * 
51174      * @return {Object} 
51175      */
51176     getCell : function(rowIndex, colIndex){
51177         var locked = this.cm.getLockedCount();
51178         var source;
51179         if(colIndex < locked){
51180             source = this.lockedBody.dom.firstChild;
51181         }else{
51182             source = this.mainBody.dom.firstChild;
51183             colIndex -= locked;
51184         }
51185         return source.rows[rowIndex].childNodes[colIndex];
51186     },
51187
51188     getCellText : function(rowIndex, colIndex){
51189         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
51190     },
51191
51192     getCellBox : function(cell){
51193         var b = this.fly(cell).getBox();
51194         if(Roo.isOpera){ // opera fails to report the Y
51195             b.y = cell.offsetTop + this.mainBody.getY();
51196         }
51197         return b;
51198     },
51199
51200     getCellIndex : function(cell){
51201         var id = String(cell.className).match(this.cellRE);
51202         if(id){
51203             return parseInt(id[1], 10);
51204         }
51205         return 0;
51206     },
51207
51208     findHeaderIndex : function(n){
51209         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
51210         return r ? this.getCellIndex(r) : false;
51211     },
51212
51213     findHeaderCell : function(n){
51214         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
51215         return r ? r : false;
51216     },
51217
51218     findRowIndex : function(n){
51219         if(!n){
51220             return false;
51221         }
51222         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
51223         return r ? r.rowIndex : false;
51224     },
51225
51226     findCellIndex : function(node){
51227         var stop = this.el.dom;
51228         while(node && node != stop){
51229             if(this.findRE.test(node.className)){
51230                 return this.getCellIndex(node);
51231             }
51232             node = node.parentNode;
51233         }
51234         return false;
51235     },
51236
51237     getColumnId : function(index){
51238         return this.cm.getColumnId(index);
51239     },
51240
51241     getSplitters : function()
51242     {
51243         if(this.splitterSelector){
51244            return Roo.DomQuery.select(this.splitterSelector);
51245         }else{
51246             return null;
51247       }
51248     },
51249
51250     getSplitter : function(index){
51251         return this.getSplitters()[index];
51252     },
51253
51254     onRowOver : function(e, t){
51255         var row;
51256         if((row = this.findRowIndex(t)) !== false){
51257             this.getRowComposite(row).addClass("x-grid-row-over");
51258         }
51259     },
51260
51261     onRowOut : function(e, t){
51262         var row;
51263         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
51264             this.getRowComposite(row).removeClass("x-grid-row-over");
51265         }
51266     },
51267
51268     renderHeaders : function(){
51269         var cm = this.cm;
51270         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
51271         var cb = [], lb = [], sb = [], lsb = [], p = {};
51272         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51273             p.cellId = "x-grid-hd-0-" + i;
51274             p.splitId = "x-grid-csplit-0-" + i;
51275             p.id = cm.getColumnId(i);
51276             p.title = cm.getColumnTooltip(i) || "";
51277             p.value = cm.getColumnHeader(i) || "";
51278             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
51279             if(!cm.isLocked(i)){
51280                 cb[cb.length] = ct.apply(p);
51281                 sb[sb.length] = st.apply(p);
51282             }else{
51283                 lb[lb.length] = ct.apply(p);
51284                 lsb[lsb.length] = st.apply(p);
51285             }
51286         }
51287         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
51288                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
51289     },
51290
51291     updateHeaders : function(){
51292         var html = this.renderHeaders();
51293         this.lockedHd.update(html[0]);
51294         this.mainHd.update(html[1]);
51295     },
51296
51297     /**
51298      * Focuses the specified row.
51299      * @param {Number} row The row index
51300      */
51301     focusRow : function(row)
51302     {
51303         //Roo.log('GridView.focusRow');
51304         var x = this.scroller.dom.scrollLeft;
51305         this.focusCell(row, 0, false);
51306         this.scroller.dom.scrollLeft = x;
51307     },
51308
51309     /**
51310      * Focuses the specified cell.
51311      * @param {Number} row The row index
51312      * @param {Number} col The column index
51313      * @param {Boolean} hscroll false to disable horizontal scrolling
51314      */
51315     focusCell : function(row, col, hscroll)
51316     {
51317         //Roo.log('GridView.focusCell');
51318         var el = this.ensureVisible(row, col, hscroll);
51319         this.focusEl.alignTo(el, "tl-tl");
51320         if(Roo.isGecko){
51321             this.focusEl.focus();
51322         }else{
51323             this.focusEl.focus.defer(1, this.focusEl);
51324         }
51325     },
51326
51327     /**
51328      * Scrolls the specified cell into view
51329      * @param {Number} row The row index
51330      * @param {Number} col The column index
51331      * @param {Boolean} hscroll false to disable horizontal scrolling
51332      */
51333     ensureVisible : function(row, col, hscroll)
51334     {
51335         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
51336         //return null; //disable for testing.
51337         if(typeof row != "number"){
51338             row = row.rowIndex;
51339         }
51340         if(row < 0 && row >= this.ds.getCount()){
51341             return  null;
51342         }
51343         col = (col !== undefined ? col : 0);
51344         var cm = this.grid.colModel;
51345         while(cm.isHidden(col)){
51346             col++;
51347         }
51348
51349         var el = this.getCell(row, col);
51350         if(!el){
51351             return null;
51352         }
51353         var c = this.scroller.dom;
51354
51355         var ctop = parseInt(el.offsetTop, 10);
51356         var cleft = parseInt(el.offsetLeft, 10);
51357         var cbot = ctop + el.offsetHeight;
51358         var cright = cleft + el.offsetWidth;
51359         
51360         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
51361         var stop = parseInt(c.scrollTop, 10);
51362         var sleft = parseInt(c.scrollLeft, 10);
51363         var sbot = stop + ch;
51364         var sright = sleft + c.clientWidth;
51365         /*
51366         Roo.log('GridView.ensureVisible:' +
51367                 ' ctop:' + ctop +
51368                 ' c.clientHeight:' + c.clientHeight +
51369                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
51370                 ' stop:' + stop +
51371                 ' cbot:' + cbot +
51372                 ' sbot:' + sbot +
51373                 ' ch:' + ch  
51374                 );
51375         */
51376         if(ctop < stop){
51377              c.scrollTop = ctop;
51378             //Roo.log("set scrolltop to ctop DISABLE?");
51379         }else if(cbot > sbot){
51380             //Roo.log("set scrolltop to cbot-ch");
51381             c.scrollTop = cbot-ch;
51382         }
51383         
51384         if(hscroll !== false){
51385             if(cleft < sleft){
51386                 c.scrollLeft = cleft;
51387             }else if(cright > sright){
51388                 c.scrollLeft = cright-c.clientWidth;
51389             }
51390         }
51391          
51392         return el;
51393     },
51394
51395     updateColumns : function(){
51396         this.grid.stopEditing();
51397         var cm = this.grid.colModel, colIds = this.getColumnIds();
51398         //var totalWidth = cm.getTotalWidth();
51399         var pos = 0;
51400         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51401             //if(cm.isHidden(i)) continue;
51402             var w = cm.getColumnWidth(i);
51403             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
51404             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
51405         }
51406         this.updateSplitters();
51407     },
51408
51409     generateRules : function(cm){
51410         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
51411         Roo.util.CSS.removeStyleSheet(rulesId);
51412         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51413             var cid = cm.getColumnId(i);
51414             var align = '';
51415             if(cm.config[i].align){
51416                 align = 'text-align:'+cm.config[i].align+';';
51417             }
51418             var hidden = '';
51419             if(cm.isHidden(i)){
51420                 hidden = 'display:none;';
51421             }
51422             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
51423             ruleBuf.push(
51424                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
51425                     this.hdSelector, cid, " {\n", align, width, "}\n",
51426                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
51427                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
51428         }
51429         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
51430     },
51431
51432     updateSplitters : function(){
51433         var cm = this.cm, s = this.getSplitters();
51434         if(s){ // splitters not created yet
51435             var pos = 0, locked = true;
51436             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51437                 if(cm.isHidden(i)) continue;
51438                 var w = cm.getColumnWidth(i); // make sure it's a number
51439                 if(!cm.isLocked(i) && locked){
51440                     pos = 0;
51441                     locked = false;
51442                 }
51443                 pos += w;
51444                 s[i].style.left = (pos-this.splitOffset) + "px";
51445             }
51446         }
51447     },
51448
51449     handleHiddenChange : function(colModel, colIndex, hidden){
51450         if(hidden){
51451             this.hideColumn(colIndex);
51452         }else{
51453             this.unhideColumn(colIndex);
51454         }
51455     },
51456
51457     hideColumn : function(colIndex){
51458         var cid = this.getColumnId(colIndex);
51459         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
51460         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
51461         if(Roo.isSafari){
51462             this.updateHeaders();
51463         }
51464         this.updateSplitters();
51465         this.layout();
51466     },
51467
51468     unhideColumn : function(colIndex){
51469         var cid = this.getColumnId(colIndex);
51470         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
51471         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
51472
51473         if(Roo.isSafari){
51474             this.updateHeaders();
51475         }
51476         this.updateSplitters();
51477         this.layout();
51478     },
51479
51480     insertRows : function(dm, firstRow, lastRow, isUpdate){
51481         if(firstRow == 0 && lastRow == dm.getCount()-1){
51482             this.refresh();
51483         }else{
51484             if(!isUpdate){
51485                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
51486             }
51487             var s = this.getScrollState();
51488             var markup = this.renderRows(firstRow, lastRow);
51489             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
51490             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
51491             this.restoreScroll(s);
51492             if(!isUpdate){
51493                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
51494                 this.syncRowHeights(firstRow, lastRow);
51495                 this.stripeRows(firstRow);
51496                 this.layout();
51497             }
51498         }
51499     },
51500
51501     bufferRows : function(markup, target, index){
51502         var before = null, trows = target.rows, tbody = target.tBodies[0];
51503         if(index < trows.length){
51504             before = trows[index];
51505         }
51506         var b = document.createElement("div");
51507         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
51508         var rows = b.firstChild.rows;
51509         for(var i = 0, len = rows.length; i < len; i++){
51510             if(before){
51511                 tbody.insertBefore(rows[0], before);
51512             }else{
51513                 tbody.appendChild(rows[0]);
51514             }
51515         }
51516         b.innerHTML = "";
51517         b = null;
51518     },
51519
51520     deleteRows : function(dm, firstRow, lastRow){
51521         if(dm.getRowCount()<1){
51522             this.fireEvent("beforerefresh", this);
51523             this.mainBody.update("");
51524             this.lockedBody.update("");
51525             this.fireEvent("refresh", this);
51526         }else{
51527             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
51528             var bt = this.getBodyTable();
51529             var tbody = bt.firstChild;
51530             var rows = bt.rows;
51531             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
51532                 tbody.removeChild(rows[firstRow]);
51533             }
51534             this.stripeRows(firstRow);
51535             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
51536         }
51537     },
51538
51539     updateRows : function(dataSource, firstRow, lastRow){
51540         var s = this.getScrollState();
51541         this.refresh();
51542         this.restoreScroll(s);
51543     },
51544
51545     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
51546         if(!noRefresh){
51547            this.refresh();
51548         }
51549         this.updateHeaderSortState();
51550     },
51551
51552     getScrollState : function(){
51553         
51554         var sb = this.scroller.dom;
51555         return {left: sb.scrollLeft, top: sb.scrollTop};
51556     },
51557
51558     stripeRows : function(startRow){
51559         if(!this.grid.stripeRows || this.ds.getCount() < 1){
51560             return;
51561         }
51562         startRow = startRow || 0;
51563         var rows = this.getBodyTable().rows;
51564         var lrows = this.getLockedTable().rows;
51565         var cls = ' x-grid-row-alt ';
51566         for(var i = startRow, len = rows.length; i < len; i++){
51567             var row = rows[i], lrow = lrows[i];
51568             var isAlt = ((i+1) % 2 == 0);
51569             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
51570             if(isAlt == hasAlt){
51571                 continue;
51572             }
51573             if(isAlt){
51574                 row.className += " x-grid-row-alt";
51575             }else{
51576                 row.className = row.className.replace("x-grid-row-alt", "");
51577             }
51578             if(lrow){
51579                 lrow.className = row.className;
51580             }
51581         }
51582     },
51583
51584     restoreScroll : function(state){
51585         //Roo.log('GridView.restoreScroll');
51586         var sb = this.scroller.dom;
51587         sb.scrollLeft = state.left;
51588         sb.scrollTop = state.top;
51589         this.syncScroll();
51590     },
51591
51592     syncScroll : function(){
51593         //Roo.log('GridView.syncScroll');
51594         var sb = this.scroller.dom;
51595         var sh = this.mainHd.dom;
51596         var bs = this.mainBody.dom;
51597         var lv = this.lockedBody.dom;
51598         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
51599         lv.scrollTop = bs.scrollTop = sb.scrollTop;
51600     },
51601
51602     handleScroll : function(e){
51603         this.syncScroll();
51604         var sb = this.scroller.dom;
51605         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
51606         e.stopEvent();
51607     },
51608
51609     handleWheel : function(e){
51610         var d = e.getWheelDelta();
51611         this.scroller.dom.scrollTop -= d*22;
51612         // set this here to prevent jumpy scrolling on large tables
51613         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
51614         e.stopEvent();
51615     },
51616
51617     renderRows : function(startRow, endRow){
51618         // pull in all the crap needed to render rows
51619         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
51620         var colCount = cm.getColumnCount();
51621
51622         if(ds.getCount() < 1){
51623             return ["", ""];
51624         }
51625
51626         // build a map for all the columns
51627         var cs = [];
51628         for(var i = 0; i < colCount; i++){
51629             var name = cm.getDataIndex(i);
51630             cs[i] = {
51631                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
51632                 renderer : cm.getRenderer(i),
51633                 id : cm.getColumnId(i),
51634                 locked : cm.isLocked(i)
51635             };
51636         }
51637
51638         startRow = startRow || 0;
51639         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
51640
51641         // records to render
51642         var rs = ds.getRange(startRow, endRow);
51643
51644         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
51645     },
51646
51647     // As much as I hate to duplicate code, this was branched because FireFox really hates
51648     // [].join("") on strings. The performance difference was substantial enough to
51649     // branch this function
51650     doRender : Roo.isGecko ?
51651             function(cs, rs, ds, startRow, colCount, stripe){
51652                 var ts = this.templates, ct = ts.cell, rt = ts.row;
51653                 // buffers
51654                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
51655                 
51656                 var hasListener = this.grid.hasListener('rowclass');
51657                 var rowcfg = {};
51658                 for(var j = 0, len = rs.length; j < len; j++){
51659                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
51660                     for(var i = 0; i < colCount; i++){
51661                         c = cs[i];
51662                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
51663                         p.id = c.id;
51664                         p.css = p.attr = "";
51665                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
51666                         if(p.value == undefined || p.value === "") p.value = "&#160;";
51667                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
51668                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
51669                         }
51670                         var markup = ct.apply(p);
51671                         if(!c.locked){
51672                             cb+= markup;
51673                         }else{
51674                             lcb+= markup;
51675                         }
51676                     }
51677                     var alt = [];
51678                     if(stripe && ((rowIndex+1) % 2 == 0)){
51679                         alt.push("x-grid-row-alt")
51680                     }
51681                     if(r.dirty){
51682                         alt.push(  " x-grid-dirty-row");
51683                     }
51684                     rp.cells = lcb;
51685                     if(this.getRowClass){
51686                         alt.push(this.getRowClass(r, rowIndex));
51687                     }
51688                     if (hasListener) {
51689                         rowcfg = {
51690                              
51691                             record: r,
51692                             rowIndex : rowIndex,
51693                             rowClass : ''
51694                         }
51695                         this.grid.fireEvent('rowclass', this, rowcfg);
51696                         alt.push(rowcfg.rowClass);
51697                     }
51698                     rp.alt = alt.join(" ");
51699                     lbuf+= rt.apply(rp);
51700                     rp.cells = cb;
51701                     buf+=  rt.apply(rp);
51702                 }
51703                 return [lbuf, buf];
51704             } :
51705             function(cs, rs, ds, startRow, colCount, stripe){
51706                 var ts = this.templates, ct = ts.cell, rt = ts.row;
51707                 // buffers
51708                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
51709                 var hasListener = this.grid.hasListener('rowclass');
51710  
51711                 var rowcfg = {};
51712                 for(var j = 0, len = rs.length; j < len; j++){
51713                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
51714                     for(var i = 0; i < colCount; i++){
51715                         c = cs[i];
51716                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
51717                         p.id = c.id;
51718                         p.css = p.attr = "";
51719                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
51720                         if(p.value == undefined || p.value === "") p.value = "&#160;";
51721                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
51722                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
51723                         }
51724                         
51725                         var markup = ct.apply(p);
51726                         if(!c.locked){
51727                             cb[cb.length] = markup;
51728                         }else{
51729                             lcb[lcb.length] = markup;
51730                         }
51731                     }
51732                     var alt = [];
51733                     if(stripe && ((rowIndex+1) % 2 == 0)){
51734                         alt.push( "x-grid-row-alt");
51735                     }
51736                     if(r.dirty){
51737                         alt.push(" x-grid-dirty-row");
51738                     }
51739                     rp.cells = lcb;
51740                     if(this.getRowClass){
51741                         alt.push( this.getRowClass(r, rowIndex));
51742                     }
51743                     if (hasListener) {
51744                         rowcfg = {
51745                              
51746                             record: r,
51747                             rowIndex : rowIndex,
51748                             rowClass : ''
51749                         }
51750                         this.grid.fireEvent('rowclass', this, rowcfg);
51751                         alt.push(rowcfg.rowClass);
51752                     }
51753                     rp.alt = alt.join(" ");
51754                     rp.cells = lcb.join("");
51755                     lbuf[lbuf.length] = rt.apply(rp);
51756                     rp.cells = cb.join("");
51757                     buf[buf.length] =  rt.apply(rp);
51758                 }
51759                 return [lbuf.join(""), buf.join("")];
51760             },
51761
51762     renderBody : function(){
51763         var markup = this.renderRows();
51764         var bt = this.templates.body;
51765         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
51766     },
51767
51768     /**
51769      * Refreshes the grid
51770      * @param {Boolean} headersToo
51771      */
51772     refresh : function(headersToo){
51773         this.fireEvent("beforerefresh", this);
51774         this.grid.stopEditing();
51775         var result = this.renderBody();
51776         this.lockedBody.update(result[0]);
51777         this.mainBody.update(result[1]);
51778         if(headersToo === true){
51779             this.updateHeaders();
51780             this.updateColumns();
51781             this.updateSplitters();
51782             this.updateHeaderSortState();
51783         }
51784         this.syncRowHeights();
51785         this.layout();
51786         this.fireEvent("refresh", this);
51787     },
51788
51789     handleColumnMove : function(cm, oldIndex, newIndex){
51790         this.indexMap = null;
51791         var s = this.getScrollState();
51792         this.refresh(true);
51793         this.restoreScroll(s);
51794         this.afterMove(newIndex);
51795     },
51796
51797     afterMove : function(colIndex){
51798         if(this.enableMoveAnim && Roo.enableFx){
51799             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
51800         }
51801         // if multisort - fix sortOrder, and reload..
51802         if (this.grid.dataSource.multiSort) {
51803             // the we can call sort again..
51804             var dm = this.grid.dataSource;
51805             var cm = this.grid.colModel;
51806             var so = [];
51807             for(var i = 0; i < cm.config.length; i++ ) {
51808                 
51809                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
51810                     continue; // dont' bother, it's not in sort list or being set.
51811                 }
51812                 
51813                 so.push(cm.config[i].dataIndex);
51814             };
51815             dm.sortOrder = so;
51816             dm.load(dm.lastOptions);
51817             
51818             
51819         }
51820         
51821     },
51822
51823     updateCell : function(dm, rowIndex, dataIndex){
51824         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
51825         if(typeof colIndex == "undefined"){ // not present in grid
51826             return;
51827         }
51828         var cm = this.grid.colModel;
51829         var cell = this.getCell(rowIndex, colIndex);
51830         var cellText = this.getCellText(rowIndex, colIndex);
51831
51832         var p = {
51833             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
51834             id : cm.getColumnId(colIndex),
51835             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
51836         };
51837         var renderer = cm.getRenderer(colIndex);
51838         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
51839         if(typeof val == "undefined" || val === "") val = "&#160;";
51840         cellText.innerHTML = val;
51841         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
51842         this.syncRowHeights(rowIndex, rowIndex);
51843     },
51844
51845     calcColumnWidth : function(colIndex, maxRowsToMeasure){
51846         var maxWidth = 0;
51847         if(this.grid.autoSizeHeaders){
51848             var h = this.getHeaderCellMeasure(colIndex);
51849             maxWidth = Math.max(maxWidth, h.scrollWidth);
51850         }
51851         var tb, index;
51852         if(this.cm.isLocked(colIndex)){
51853             tb = this.getLockedTable();
51854             index = colIndex;
51855         }else{
51856             tb = this.getBodyTable();
51857             index = colIndex - this.cm.getLockedCount();
51858         }
51859         if(tb && tb.rows){
51860             var rows = tb.rows;
51861             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
51862             for(var i = 0; i < stopIndex; i++){
51863                 var cell = rows[i].childNodes[index].firstChild;
51864                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
51865             }
51866         }
51867         return maxWidth + /*margin for error in IE*/ 5;
51868     },
51869     /**
51870      * Autofit a column to its content.
51871      * @param {Number} colIndex
51872      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
51873      */
51874      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
51875          if(this.cm.isHidden(colIndex)){
51876              return; // can't calc a hidden column
51877          }
51878         if(forceMinSize){
51879             var cid = this.cm.getColumnId(colIndex);
51880             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
51881            if(this.grid.autoSizeHeaders){
51882                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
51883            }
51884         }
51885         var newWidth = this.calcColumnWidth(colIndex);
51886         this.cm.setColumnWidth(colIndex,
51887             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
51888         if(!suppressEvent){
51889             this.grid.fireEvent("columnresize", colIndex, newWidth);
51890         }
51891     },
51892
51893     /**
51894      * Autofits all columns to their content and then expands to fit any extra space in the grid
51895      */
51896      autoSizeColumns : function(){
51897         var cm = this.grid.colModel;
51898         var colCount = cm.getColumnCount();
51899         for(var i = 0; i < colCount; i++){
51900             this.autoSizeColumn(i, true, true);
51901         }
51902         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
51903             this.fitColumns();
51904         }else{
51905             this.updateColumns();
51906             this.layout();
51907         }
51908     },
51909
51910     /**
51911      * Autofits all columns to the grid's width proportionate with their current size
51912      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
51913      */
51914     fitColumns : function(reserveScrollSpace){
51915         var cm = this.grid.colModel;
51916         var colCount = cm.getColumnCount();
51917         var cols = [];
51918         var width = 0;
51919         var i, w;
51920         for (i = 0; i < colCount; i++){
51921             if(!cm.isHidden(i) && !cm.isFixed(i)){
51922                 w = cm.getColumnWidth(i);
51923                 cols.push(i);
51924                 cols.push(w);
51925                 width += w;
51926             }
51927         }
51928         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
51929         if(reserveScrollSpace){
51930             avail -= 17;
51931         }
51932         var frac = (avail - cm.getTotalWidth())/width;
51933         while (cols.length){
51934             w = cols.pop();
51935             i = cols.pop();
51936             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
51937         }
51938         this.updateColumns();
51939         this.layout();
51940     },
51941
51942     onRowSelect : function(rowIndex){
51943         var row = this.getRowComposite(rowIndex);
51944         row.addClass("x-grid-row-selected");
51945     },
51946
51947     onRowDeselect : function(rowIndex){
51948         var row = this.getRowComposite(rowIndex);
51949         row.removeClass("x-grid-row-selected");
51950     },
51951
51952     onCellSelect : function(row, col){
51953         var cell = this.getCell(row, col);
51954         if(cell){
51955             Roo.fly(cell).addClass("x-grid-cell-selected");
51956         }
51957     },
51958
51959     onCellDeselect : function(row, col){
51960         var cell = this.getCell(row, col);
51961         if(cell){
51962             Roo.fly(cell).removeClass("x-grid-cell-selected");
51963         }
51964     },
51965
51966     updateHeaderSortState : function(){
51967         
51968         // sort state can be single { field: xxx, direction : yyy}
51969         // or   { xxx=>ASC , yyy : DESC ..... }
51970         
51971         var mstate = {};
51972         if (!this.ds.multiSort) { 
51973             var state = this.ds.getSortState();
51974             if(!state){
51975                 return;
51976             }
51977             mstate[state.field] = state.direction;
51978             // FIXME... - this is not used here.. but might be elsewhere..
51979             this.sortState = state;
51980             
51981         } else {
51982             mstate = this.ds.sortToggle;
51983         }
51984         //remove existing sort classes..
51985         
51986         var sc = this.sortClasses;
51987         var hds = this.el.select(this.headerSelector).removeClass(sc);
51988         
51989         for(var f in mstate) {
51990         
51991             var sortColumn = this.cm.findColumnIndex(f);
51992             
51993             if(sortColumn != -1){
51994                 var sortDir = mstate[f];        
51995                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
51996             }
51997         }
51998         
51999          
52000         
52001     },
52002
52003
52004     handleHeaderClick : function(g, index){
52005         if(this.headersDisabled){
52006             return;
52007         }
52008         var dm = g.dataSource, cm = g.colModel;
52009         if(!cm.isSortable(index)){
52010             return;
52011         }
52012         g.stopEditing();
52013         
52014         if (dm.multiSort) {
52015             // update the sortOrder
52016             var so = [];
52017             for(var i = 0; i < cm.config.length; i++ ) {
52018                 
52019                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
52020                     continue; // dont' bother, it's not in sort list or being set.
52021                 }
52022                 
52023                 so.push(cm.config[i].dataIndex);
52024             };
52025             dm.sortOrder = so;
52026         }
52027         
52028         
52029         dm.sort(cm.getDataIndex(index));
52030     },
52031
52032
52033     destroy : function(){
52034         if(this.colMenu){
52035             this.colMenu.removeAll();
52036             Roo.menu.MenuMgr.unregister(this.colMenu);
52037             this.colMenu.getEl().remove();
52038             delete this.colMenu;
52039         }
52040         if(this.hmenu){
52041             this.hmenu.removeAll();
52042             Roo.menu.MenuMgr.unregister(this.hmenu);
52043             this.hmenu.getEl().remove();
52044             delete this.hmenu;
52045         }
52046         if(this.grid.enableColumnMove){
52047             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
52048             if(dds){
52049                 for(var dd in dds){
52050                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
52051                         var elid = dds[dd].dragElId;
52052                         dds[dd].unreg();
52053                         Roo.get(elid).remove();
52054                     } else if(dds[dd].config.isTarget){
52055                         dds[dd].proxyTop.remove();
52056                         dds[dd].proxyBottom.remove();
52057                         dds[dd].unreg();
52058                     }
52059                     if(Roo.dd.DDM.locationCache[dd]){
52060                         delete Roo.dd.DDM.locationCache[dd];
52061                     }
52062                 }
52063                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
52064             }
52065         }
52066         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
52067         this.bind(null, null);
52068         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
52069     },
52070
52071     handleLockChange : function(){
52072         this.refresh(true);
52073     },
52074
52075     onDenyColumnLock : function(){
52076
52077     },
52078
52079     onDenyColumnHide : function(){
52080
52081     },
52082
52083     handleHdMenuClick : function(item){
52084         var index = this.hdCtxIndex;
52085         var cm = this.cm, ds = this.ds;
52086         switch(item.id){
52087             case "asc":
52088                 ds.sort(cm.getDataIndex(index), "ASC");
52089                 break;
52090             case "desc":
52091                 ds.sort(cm.getDataIndex(index), "DESC");
52092                 break;
52093             case "lock":
52094                 var lc = cm.getLockedCount();
52095                 if(cm.getColumnCount(true) <= lc+1){
52096                     this.onDenyColumnLock();
52097                     return;
52098                 }
52099                 if(lc != index){
52100                     cm.setLocked(index, true, true);
52101                     cm.moveColumn(index, lc);
52102                     this.grid.fireEvent("columnmove", index, lc);
52103                 }else{
52104                     cm.setLocked(index, true);
52105                 }
52106             break;
52107             case "unlock":
52108                 var lc = cm.getLockedCount();
52109                 if((lc-1) != index){
52110                     cm.setLocked(index, false, true);
52111                     cm.moveColumn(index, lc-1);
52112                     this.grid.fireEvent("columnmove", index, lc-1);
52113                 }else{
52114                     cm.setLocked(index, false);
52115                 }
52116             break;
52117             default:
52118                 index = cm.getIndexById(item.id.substr(4));
52119                 if(index != -1){
52120                     if(item.checked && cm.getColumnCount(true) <= 1){
52121                         this.onDenyColumnHide();
52122                         return false;
52123                     }
52124                     cm.setHidden(index, item.checked);
52125                 }
52126         }
52127         return true;
52128     },
52129
52130     beforeColMenuShow : function(){
52131         var cm = this.cm,  colCount = cm.getColumnCount();
52132         this.colMenu.removeAll();
52133         for(var i = 0; i < colCount; i++){
52134             this.colMenu.add(new Roo.menu.CheckItem({
52135                 id: "col-"+cm.getColumnId(i),
52136                 text: cm.getColumnHeader(i),
52137                 checked: !cm.isHidden(i),
52138                 hideOnClick:false
52139             }));
52140         }
52141     },
52142
52143     handleHdCtx : function(g, index, e){
52144         e.stopEvent();
52145         var hd = this.getHeaderCell(index);
52146         this.hdCtxIndex = index;
52147         var ms = this.hmenu.items, cm = this.cm;
52148         ms.get("asc").setDisabled(!cm.isSortable(index));
52149         ms.get("desc").setDisabled(!cm.isSortable(index));
52150         if(this.grid.enableColLock !== false){
52151             ms.get("lock").setDisabled(cm.isLocked(index));
52152             ms.get("unlock").setDisabled(!cm.isLocked(index));
52153         }
52154         this.hmenu.show(hd, "tl-bl");
52155     },
52156
52157     handleHdOver : function(e){
52158         var hd = this.findHeaderCell(e.getTarget());
52159         if(hd && !this.headersDisabled){
52160             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
52161                this.fly(hd).addClass("x-grid-hd-over");
52162             }
52163         }
52164     },
52165
52166     handleHdOut : function(e){
52167         var hd = this.findHeaderCell(e.getTarget());
52168         if(hd){
52169             this.fly(hd).removeClass("x-grid-hd-over");
52170         }
52171     },
52172
52173     handleSplitDblClick : function(e, t){
52174         var i = this.getCellIndex(t);
52175         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
52176             this.autoSizeColumn(i, true);
52177             this.layout();
52178         }
52179     },
52180
52181     render : function(){
52182
52183         var cm = this.cm;
52184         var colCount = cm.getColumnCount();
52185
52186         if(this.grid.monitorWindowResize === true){
52187             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52188         }
52189         var header = this.renderHeaders();
52190         var body = this.templates.body.apply({rows:""});
52191         var html = this.templates.master.apply({
52192             lockedBody: body,
52193             body: body,
52194             lockedHeader: header[0],
52195             header: header[1]
52196         });
52197
52198         //this.updateColumns();
52199
52200         this.grid.getGridEl().dom.innerHTML = html;
52201
52202         this.initElements();
52203         
52204         // a kludge to fix the random scolling effect in webkit
52205         this.el.on("scroll", function() {
52206             this.el.dom.scrollTop=0; // hopefully not recursive..
52207         },this);
52208
52209         this.scroller.on("scroll", this.handleScroll, this);
52210         this.lockedBody.on("mousewheel", this.handleWheel, this);
52211         this.mainBody.on("mousewheel", this.handleWheel, this);
52212
52213         this.mainHd.on("mouseover", this.handleHdOver, this);
52214         this.mainHd.on("mouseout", this.handleHdOut, this);
52215         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
52216                 {delegate: "."+this.splitClass});
52217
52218         this.lockedHd.on("mouseover", this.handleHdOver, this);
52219         this.lockedHd.on("mouseout", this.handleHdOut, this);
52220         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
52221                 {delegate: "."+this.splitClass});
52222
52223         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
52224             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
52225         }
52226
52227         this.updateSplitters();
52228
52229         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
52230             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
52231             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
52232         }
52233
52234         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
52235             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
52236             this.hmenu.add(
52237                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
52238                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
52239             );
52240             if(this.grid.enableColLock !== false){
52241                 this.hmenu.add('-',
52242                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
52243                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
52244                 );
52245             }
52246             if(this.grid.enableColumnHide !== false){
52247
52248                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
52249                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
52250                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
52251
52252                 this.hmenu.add('-',
52253                     {id:"columns", text: this.columnsText, menu: this.colMenu}
52254                 );
52255             }
52256             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
52257
52258             this.grid.on("headercontextmenu", this.handleHdCtx, this);
52259         }
52260
52261         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
52262             this.dd = new Roo.grid.GridDragZone(this.grid, {
52263                 ddGroup : this.grid.ddGroup || 'GridDD'
52264             });
52265             
52266         }
52267
52268         /*
52269         for(var i = 0; i < colCount; i++){
52270             if(cm.isHidden(i)){
52271                 this.hideColumn(i);
52272             }
52273             if(cm.config[i].align){
52274                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
52275                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
52276             }
52277         }*/
52278         
52279         this.updateHeaderSortState();
52280
52281         this.beforeInitialResize();
52282         this.layout(true);
52283
52284         // two part rendering gives faster view to the user
52285         this.renderPhase2.defer(1, this);
52286     },
52287
52288     renderPhase2 : function(){
52289         // render the rows now
52290         this.refresh();
52291         if(this.grid.autoSizeColumns){
52292             this.autoSizeColumns();
52293         }
52294     },
52295
52296     beforeInitialResize : function(){
52297
52298     },
52299
52300     onColumnSplitterMoved : function(i, w){
52301         this.userResized = true;
52302         var cm = this.grid.colModel;
52303         cm.setColumnWidth(i, w, true);
52304         var cid = cm.getColumnId(i);
52305         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
52306         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
52307         this.updateSplitters();
52308         this.layout();
52309         this.grid.fireEvent("columnresize", i, w);
52310     },
52311
52312     syncRowHeights : function(startIndex, endIndex){
52313         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
52314             startIndex = startIndex || 0;
52315             var mrows = this.getBodyTable().rows;
52316             var lrows = this.getLockedTable().rows;
52317             var len = mrows.length-1;
52318             endIndex = Math.min(endIndex || len, len);
52319             for(var i = startIndex; i <= endIndex; i++){
52320                 var m = mrows[i], l = lrows[i];
52321                 var h = Math.max(m.offsetHeight, l.offsetHeight);
52322                 m.style.height = l.style.height = h + "px";
52323             }
52324         }
52325     },
52326
52327     layout : function(initialRender, is2ndPass){
52328         var g = this.grid;
52329         var auto = g.autoHeight;
52330         var scrollOffset = 16;
52331         var c = g.getGridEl(), cm = this.cm,
52332                 expandCol = g.autoExpandColumn,
52333                 gv = this;
52334         //c.beginMeasure();
52335
52336         if(!c.dom.offsetWidth){ // display:none?
52337             if(initialRender){
52338                 this.lockedWrap.show();
52339                 this.mainWrap.show();
52340             }
52341             return;
52342         }
52343
52344         var hasLock = this.cm.isLocked(0);
52345
52346         var tbh = this.headerPanel.getHeight();
52347         var bbh = this.footerPanel.getHeight();
52348
52349         if(auto){
52350             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
52351             var newHeight = ch + c.getBorderWidth("tb");
52352             if(g.maxHeight){
52353                 newHeight = Math.min(g.maxHeight, newHeight);
52354             }
52355             c.setHeight(newHeight);
52356         }
52357
52358         if(g.autoWidth){
52359             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
52360         }
52361
52362         var s = this.scroller;
52363
52364         var csize = c.getSize(true);
52365
52366         this.el.setSize(csize.width, csize.height);
52367
52368         this.headerPanel.setWidth(csize.width);
52369         this.footerPanel.setWidth(csize.width);
52370
52371         var hdHeight = this.mainHd.getHeight();
52372         var vw = csize.width;
52373         var vh = csize.height - (tbh + bbh);
52374
52375         s.setSize(vw, vh);
52376
52377         var bt = this.getBodyTable();
52378         var ltWidth = hasLock ?
52379                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
52380
52381         var scrollHeight = bt.offsetHeight;
52382         var scrollWidth = ltWidth + bt.offsetWidth;
52383         var vscroll = false, hscroll = false;
52384
52385         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
52386
52387         var lw = this.lockedWrap, mw = this.mainWrap;
52388         var lb = this.lockedBody, mb = this.mainBody;
52389
52390         setTimeout(function(){
52391             var t = s.dom.offsetTop;
52392             var w = s.dom.clientWidth,
52393                 h = s.dom.clientHeight;
52394
52395             lw.setTop(t);
52396             lw.setSize(ltWidth, h);
52397
52398             mw.setLeftTop(ltWidth, t);
52399             mw.setSize(w-ltWidth, h);
52400
52401             lb.setHeight(h-hdHeight);
52402             mb.setHeight(h-hdHeight);
52403
52404             if(is2ndPass !== true && !gv.userResized && expandCol){
52405                 // high speed resize without full column calculation
52406                 
52407                 var ci = cm.getIndexById(expandCol);
52408                 if (ci < 0) {
52409                     ci = cm.findColumnIndex(expandCol);
52410                 }
52411                 ci = Math.max(0, ci); // make sure it's got at least the first col.
52412                 var expandId = cm.getColumnId(ci);
52413                 var  tw = cm.getTotalWidth(false);
52414                 var currentWidth = cm.getColumnWidth(ci);
52415                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
52416                 if(currentWidth != cw){
52417                     cm.setColumnWidth(ci, cw, true);
52418                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
52419                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
52420                     gv.updateSplitters();
52421                     gv.layout(false, true);
52422                 }
52423             }
52424
52425             if(initialRender){
52426                 lw.show();
52427                 mw.show();
52428             }
52429             //c.endMeasure();
52430         }, 10);
52431     },
52432
52433     onWindowResize : function(){
52434         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
52435             return;
52436         }
52437         this.layout();
52438     },
52439
52440     appendFooter : function(parentEl){
52441         return null;
52442     },
52443
52444     sortAscText : "Sort Ascending",
52445     sortDescText : "Sort Descending",
52446     lockText : "Lock Column",
52447     unlockText : "Unlock Column",
52448     columnsText : "Columns"
52449 });
52450
52451
52452 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
52453     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
52454     this.proxy.el.addClass('x-grid3-col-dd');
52455 };
52456
52457 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
52458     handleMouseDown : function(e){
52459
52460     },
52461
52462     callHandleMouseDown : function(e){
52463         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
52464     }
52465 });
52466 /*
52467  * Based on:
52468  * Ext JS Library 1.1.1
52469  * Copyright(c) 2006-2007, Ext JS, LLC.
52470  *
52471  * Originally Released Under LGPL - original licence link has changed is not relivant.
52472  *
52473  * Fork - LGPL
52474  * <script type="text/javascript">
52475  */
52476  
52477 // private
52478 // This is a support class used internally by the Grid components
52479 Roo.grid.SplitDragZone = function(grid, hd, hd2){
52480     this.grid = grid;
52481     this.view = grid.getView();
52482     this.proxy = this.view.resizeProxy;
52483     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
52484         "gridSplitters" + this.grid.getGridEl().id, {
52485         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
52486     });
52487     this.setHandleElId(Roo.id(hd));
52488     this.setOuterHandleElId(Roo.id(hd2));
52489     this.scroll = false;
52490 };
52491 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
52492     fly: Roo.Element.fly,
52493
52494     b4StartDrag : function(x, y){
52495         this.view.headersDisabled = true;
52496         this.proxy.setHeight(this.view.mainWrap.getHeight());
52497         var w = this.cm.getColumnWidth(this.cellIndex);
52498         var minw = Math.max(w-this.grid.minColumnWidth, 0);
52499         this.resetConstraints();
52500         this.setXConstraint(minw, 1000);
52501         this.setYConstraint(0, 0);
52502         this.minX = x - minw;
52503         this.maxX = x + 1000;
52504         this.startPos = x;
52505         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
52506     },
52507
52508
52509     handleMouseDown : function(e){
52510         ev = Roo.EventObject.setEvent(e);
52511         var t = this.fly(ev.getTarget());
52512         if(t.hasClass("x-grid-split")){
52513             this.cellIndex = this.view.getCellIndex(t.dom);
52514             this.split = t.dom;
52515             this.cm = this.grid.colModel;
52516             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
52517                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
52518             }
52519         }
52520     },
52521
52522     endDrag : function(e){
52523         this.view.headersDisabled = false;
52524         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
52525         var diff = endX - this.startPos;
52526         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
52527     },
52528
52529     autoOffset : function(){
52530         this.setDelta(0,0);
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 // private
52544 // This is a support class used internally by the Grid components
52545 Roo.grid.GridDragZone = function(grid, config){
52546     this.view = grid.getView();
52547     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
52548     if(this.view.lockedBody){
52549         this.setHandleElId(Roo.id(this.view.mainBody.dom));
52550         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
52551     }
52552     this.scroll = false;
52553     this.grid = grid;
52554     this.ddel = document.createElement('div');
52555     this.ddel.className = 'x-grid-dd-wrap';
52556 };
52557
52558 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
52559     ddGroup : "GridDD",
52560
52561     getDragData : function(e){
52562         var t = Roo.lib.Event.getTarget(e);
52563         var rowIndex = this.view.findRowIndex(t);
52564         var sm = this.grid.selModel;
52565             
52566         //Roo.log(rowIndex);
52567         
52568         if (sm.getSelectedCell) {
52569             // cell selection..
52570             if (!sm.getSelectedCell()) {
52571                 return false;
52572             }
52573             if (rowIndex != sm.getSelectedCell()[0]) {
52574                 return false;
52575             }
52576         
52577         }
52578         
52579         if(rowIndex !== false){
52580             
52581             // if editorgrid.. 
52582             
52583             
52584             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
52585                
52586             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
52587               //  
52588             //}
52589             if (e.hasModifier()){
52590                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
52591             }
52592             
52593             Roo.log("getDragData");
52594             
52595             return {
52596                 grid: this.grid,
52597                 ddel: this.ddel,
52598                 rowIndex: rowIndex,
52599                 selections:sm.getSelections ? sm.getSelections() : (
52600                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
52601                 )
52602             };
52603         }
52604         return false;
52605     },
52606
52607     onInitDrag : function(e){
52608         var data = this.dragData;
52609         this.ddel.innerHTML = this.grid.getDragDropText();
52610         this.proxy.update(this.ddel);
52611         // fire start drag?
52612     },
52613
52614     afterRepair : function(){
52615         this.dragging = false;
52616     },
52617
52618     getRepairXY : function(e, data){
52619         return false;
52620     },
52621
52622     onEndDrag : function(data, e){
52623         // fire end drag?
52624     },
52625
52626     onValidDrop : function(dd, e, id){
52627         // fire drag drop?
52628         this.hideProxy();
52629     },
52630
52631     beforeInvalidDrop : function(e, id){
52632
52633     }
52634 });/*
52635  * Based on:
52636  * Ext JS Library 1.1.1
52637  * Copyright(c) 2006-2007, Ext JS, LLC.
52638  *
52639  * Originally Released Under LGPL - original licence link has changed is not relivant.
52640  *
52641  * Fork - LGPL
52642  * <script type="text/javascript">
52643  */
52644  
52645
52646 /**
52647  * @class Roo.grid.ColumnModel
52648  * @extends Roo.util.Observable
52649  * This is the default implementation of a ColumnModel used by the Grid. It defines
52650  * the columns in the grid.
52651  * <br>Usage:<br>
52652  <pre><code>
52653  var colModel = new Roo.grid.ColumnModel([
52654         {header: "Ticker", width: 60, sortable: true, locked: true},
52655         {header: "Company Name", width: 150, sortable: true},
52656         {header: "Market Cap.", width: 100, sortable: true},
52657         {header: "$ Sales", width: 100, sortable: true, renderer: money},
52658         {header: "Employees", width: 100, sortable: true, resizable: false}
52659  ]);
52660  </code></pre>
52661  * <p>
52662  
52663  * The config options listed for this class are options which may appear in each
52664  * individual column definition.
52665  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
52666  * @constructor
52667  * @param {Object} config An Array of column config objects. See this class's
52668  * config objects for details.
52669 */
52670 Roo.grid.ColumnModel = function(config){
52671         /**
52672      * The config passed into the constructor
52673      */
52674     this.config = config;
52675     this.lookup = {};
52676
52677     // if no id, create one
52678     // if the column does not have a dataIndex mapping,
52679     // map it to the order it is in the config
52680     for(var i = 0, len = config.length; i < len; i++){
52681         var c = config[i];
52682         if(typeof c.dataIndex == "undefined"){
52683             c.dataIndex = i;
52684         }
52685         if(typeof c.renderer == "string"){
52686             c.renderer = Roo.util.Format[c.renderer];
52687         }
52688         if(typeof c.id == "undefined"){
52689             c.id = Roo.id();
52690         }
52691         if(c.editor && c.editor.xtype){
52692             c.editor  = Roo.factory(c.editor, Roo.grid);
52693         }
52694         if(c.editor && c.editor.isFormField){
52695             c.editor = new Roo.grid.GridEditor(c.editor);
52696         }
52697         this.lookup[c.id] = c;
52698     }
52699
52700     /**
52701      * The width of columns which have no width specified (defaults to 100)
52702      * @type Number
52703      */
52704     this.defaultWidth = 100;
52705
52706     /**
52707      * Default sortable of columns which have no sortable specified (defaults to false)
52708      * @type Boolean
52709      */
52710     this.defaultSortable = false;
52711
52712     this.addEvents({
52713         /**
52714              * @event widthchange
52715              * Fires when the width of a column changes.
52716              * @param {ColumnModel} this
52717              * @param {Number} columnIndex The column index
52718              * @param {Number} newWidth The new width
52719              */
52720             "widthchange": true,
52721         /**
52722              * @event headerchange
52723              * Fires when the text of a header changes.
52724              * @param {ColumnModel} this
52725              * @param {Number} columnIndex The column index
52726              * @param {Number} newText The new header text
52727              */
52728             "headerchange": true,
52729         /**
52730              * @event hiddenchange
52731              * Fires when a column is hidden or "unhidden".
52732              * @param {ColumnModel} this
52733              * @param {Number} columnIndex The column index
52734              * @param {Boolean} hidden true if hidden, false otherwise
52735              */
52736             "hiddenchange": true,
52737             /**
52738          * @event columnmoved
52739          * Fires when a column is moved.
52740          * @param {ColumnModel} this
52741          * @param {Number} oldIndex
52742          * @param {Number} newIndex
52743          */
52744         "columnmoved" : true,
52745         /**
52746          * @event columlockchange
52747          * Fires when a column's locked state is changed
52748          * @param {ColumnModel} this
52749          * @param {Number} colIndex
52750          * @param {Boolean} locked true if locked
52751          */
52752         "columnlockchange" : true
52753     });
52754     Roo.grid.ColumnModel.superclass.constructor.call(this);
52755 };
52756 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
52757     /**
52758      * @cfg {String} header The header text to display in the Grid view.
52759      */
52760     /**
52761      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
52762      * {@link Roo.data.Record} definition from which to draw the column's value. If not
52763      * specified, the column's index is used as an index into the Record's data Array.
52764      */
52765     /**
52766      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
52767      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
52768      */
52769     /**
52770      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
52771      * Defaults to the value of the {@link #defaultSortable} property.
52772      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
52773      */
52774     /**
52775      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
52776      */
52777     /**
52778      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
52779      */
52780     /**
52781      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
52782      */
52783     /**
52784      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
52785      */
52786     /**
52787      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
52788      * given the cell's data value. See {@link #setRenderer}. If not specified, the
52789      * default renderer uses the raw data value.
52790      */
52791        /**
52792      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
52793      */
52794     /**
52795      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
52796      */
52797
52798     /**
52799      * Returns the id of the column at the specified index.
52800      * @param {Number} index The column index
52801      * @return {String} the id
52802      */
52803     getColumnId : function(index){
52804         return this.config[index].id;
52805     },
52806
52807     /**
52808      * Returns the column for a specified id.
52809      * @param {String} id The column id
52810      * @return {Object} the column
52811      */
52812     getColumnById : function(id){
52813         return this.lookup[id];
52814     },
52815
52816     
52817     /**
52818      * Returns the column for a specified dataIndex.
52819      * @param {String} dataIndex The column dataIndex
52820      * @return {Object|Boolean} the column or false if not found
52821      */
52822     getColumnByDataIndex: function(dataIndex){
52823         var index = this.findColumnIndex(dataIndex);
52824         return index > -1 ? this.config[index] : false;
52825     },
52826     
52827     /**
52828      * Returns the index for a specified column id.
52829      * @param {String} id The column id
52830      * @return {Number} the index, or -1 if not found
52831      */
52832     getIndexById : function(id){
52833         for(var i = 0, len = this.config.length; i < len; i++){
52834             if(this.config[i].id == id){
52835                 return i;
52836             }
52837         }
52838         return -1;
52839     },
52840     
52841     /**
52842      * Returns the index for a specified column dataIndex.
52843      * @param {String} dataIndex The column dataIndex
52844      * @return {Number} the index, or -1 if not found
52845      */
52846     
52847     findColumnIndex : function(dataIndex){
52848         for(var i = 0, len = this.config.length; i < len; i++){
52849             if(this.config[i].dataIndex == dataIndex){
52850                 return i;
52851             }
52852         }
52853         return -1;
52854     },
52855     
52856     
52857     moveColumn : function(oldIndex, newIndex){
52858         var c = this.config[oldIndex];
52859         this.config.splice(oldIndex, 1);
52860         this.config.splice(newIndex, 0, c);
52861         this.dataMap = null;
52862         this.fireEvent("columnmoved", this, oldIndex, newIndex);
52863     },
52864
52865     isLocked : function(colIndex){
52866         return this.config[colIndex].locked === true;
52867     },
52868
52869     setLocked : function(colIndex, value, suppressEvent){
52870         if(this.isLocked(colIndex) == value){
52871             return;
52872         }
52873         this.config[colIndex].locked = value;
52874         if(!suppressEvent){
52875             this.fireEvent("columnlockchange", this, colIndex, value);
52876         }
52877     },
52878
52879     getTotalLockedWidth : function(){
52880         var totalWidth = 0;
52881         for(var i = 0; i < this.config.length; i++){
52882             if(this.isLocked(i) && !this.isHidden(i)){
52883                 this.totalWidth += this.getColumnWidth(i);
52884             }
52885         }
52886         return totalWidth;
52887     },
52888
52889     getLockedCount : function(){
52890         for(var i = 0, len = this.config.length; i < len; i++){
52891             if(!this.isLocked(i)){
52892                 return i;
52893             }
52894         }
52895     },
52896
52897     /**
52898      * Returns the number of columns.
52899      * @return {Number}
52900      */
52901     getColumnCount : function(visibleOnly){
52902         if(visibleOnly === true){
52903             var c = 0;
52904             for(var i = 0, len = this.config.length; i < len; i++){
52905                 if(!this.isHidden(i)){
52906                     c++;
52907                 }
52908             }
52909             return c;
52910         }
52911         return this.config.length;
52912     },
52913
52914     /**
52915      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
52916      * @param {Function} fn
52917      * @param {Object} scope (optional)
52918      * @return {Array} result
52919      */
52920     getColumnsBy : function(fn, scope){
52921         var r = [];
52922         for(var i = 0, len = this.config.length; i < len; i++){
52923             var c = this.config[i];
52924             if(fn.call(scope||this, c, i) === true){
52925                 r[r.length] = c;
52926             }
52927         }
52928         return r;
52929     },
52930
52931     /**
52932      * Returns true if the specified column is sortable.
52933      * @param {Number} col The column index
52934      * @return {Boolean}
52935      */
52936     isSortable : function(col){
52937         if(typeof this.config[col].sortable == "undefined"){
52938             return this.defaultSortable;
52939         }
52940         return this.config[col].sortable;
52941     },
52942
52943     /**
52944      * Returns the rendering (formatting) function defined for the column.
52945      * @param {Number} col The column index.
52946      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
52947      */
52948     getRenderer : function(col){
52949         if(!this.config[col].renderer){
52950             return Roo.grid.ColumnModel.defaultRenderer;
52951         }
52952         return this.config[col].renderer;
52953     },
52954
52955     /**
52956      * Sets the rendering (formatting) function for a column.
52957      * @param {Number} col The column index
52958      * @param {Function} fn The function to use to process the cell's raw data
52959      * to return HTML markup for the grid view. The render function is called with
52960      * the following parameters:<ul>
52961      * <li>Data value.</li>
52962      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
52963      * <li>css A CSS style string to apply to the table cell.</li>
52964      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
52965      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
52966      * <li>Row index</li>
52967      * <li>Column index</li>
52968      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
52969      */
52970     setRenderer : function(col, fn){
52971         this.config[col].renderer = fn;
52972     },
52973
52974     /**
52975      * Returns the width for the specified column.
52976      * @param {Number} col The column index
52977      * @return {Number}
52978      */
52979     getColumnWidth : function(col){
52980         return this.config[col].width * 1 || this.defaultWidth;
52981     },
52982
52983     /**
52984      * Sets the width for a column.
52985      * @param {Number} col The column index
52986      * @param {Number} width The new width
52987      */
52988     setColumnWidth : function(col, width, suppressEvent){
52989         this.config[col].width = width;
52990         this.totalWidth = null;
52991         if(!suppressEvent){
52992              this.fireEvent("widthchange", this, col, width);
52993         }
52994     },
52995
52996     /**
52997      * Returns the total width of all columns.
52998      * @param {Boolean} includeHidden True to include hidden column widths
52999      * @return {Number}
53000      */
53001     getTotalWidth : function(includeHidden){
53002         if(!this.totalWidth){
53003             this.totalWidth = 0;
53004             for(var i = 0, len = this.config.length; i < len; i++){
53005                 if(includeHidden || !this.isHidden(i)){
53006                     this.totalWidth += this.getColumnWidth(i);
53007                 }
53008             }
53009         }
53010         return this.totalWidth;
53011     },
53012
53013     /**
53014      * Returns the header for the specified column.
53015      * @param {Number} col The column index
53016      * @return {String}
53017      */
53018     getColumnHeader : function(col){
53019         return this.config[col].header;
53020     },
53021
53022     /**
53023      * Sets the header for a column.
53024      * @param {Number} col The column index
53025      * @param {String} header The new header
53026      */
53027     setColumnHeader : function(col, header){
53028         this.config[col].header = header;
53029         this.fireEvent("headerchange", this, col, header);
53030     },
53031
53032     /**
53033      * Returns the tooltip for the specified column.
53034      * @param {Number} col The column index
53035      * @return {String}
53036      */
53037     getColumnTooltip : function(col){
53038             return this.config[col].tooltip;
53039     },
53040     /**
53041      * Sets the tooltip for a column.
53042      * @param {Number} col The column index
53043      * @param {String} tooltip The new tooltip
53044      */
53045     setColumnTooltip : function(col, tooltip){
53046             this.config[col].tooltip = tooltip;
53047     },
53048
53049     /**
53050      * Returns the dataIndex for the specified column.
53051      * @param {Number} col The column index
53052      * @return {Number}
53053      */
53054     getDataIndex : function(col){
53055         return this.config[col].dataIndex;
53056     },
53057
53058     /**
53059      * Sets the dataIndex for a column.
53060      * @param {Number} col The column index
53061      * @param {Number} dataIndex The new dataIndex
53062      */
53063     setDataIndex : function(col, dataIndex){
53064         this.config[col].dataIndex = dataIndex;
53065     },
53066
53067     
53068     
53069     /**
53070      * Returns true if the cell is editable.
53071      * @param {Number} colIndex The column index
53072      * @param {Number} rowIndex The row index
53073      * @return {Boolean}
53074      */
53075     isCellEditable : function(colIndex, rowIndex){
53076         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
53077     },
53078
53079     /**
53080      * Returns the editor defined for the cell/column.
53081      * return false or null to disable editing.
53082      * @param {Number} colIndex The column index
53083      * @param {Number} rowIndex The row index
53084      * @return {Object}
53085      */
53086     getCellEditor : function(colIndex, rowIndex){
53087         return this.config[colIndex].editor;
53088     },
53089
53090     /**
53091      * Sets if a column is editable.
53092      * @param {Number} col The column index
53093      * @param {Boolean} editable True if the column is editable
53094      */
53095     setEditable : function(col, editable){
53096         this.config[col].editable = editable;
53097     },
53098
53099
53100     /**
53101      * Returns true if the column is hidden.
53102      * @param {Number} colIndex The column index
53103      * @return {Boolean}
53104      */
53105     isHidden : function(colIndex){
53106         return this.config[colIndex].hidden;
53107     },
53108
53109
53110     /**
53111      * Returns true if the column width cannot be changed
53112      */
53113     isFixed : function(colIndex){
53114         return this.config[colIndex].fixed;
53115     },
53116
53117     /**
53118      * Returns true if the column can be resized
53119      * @return {Boolean}
53120      */
53121     isResizable : function(colIndex){
53122         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
53123     },
53124     /**
53125      * Sets if a column is hidden.
53126      * @param {Number} colIndex The column index
53127      * @param {Boolean} hidden True if the column is hidden
53128      */
53129     setHidden : function(colIndex, hidden){
53130         this.config[colIndex].hidden = hidden;
53131         this.totalWidth = null;
53132         this.fireEvent("hiddenchange", this, colIndex, hidden);
53133     },
53134
53135     /**
53136      * Sets the editor for a column.
53137      * @param {Number} col The column index
53138      * @param {Object} editor The editor object
53139      */
53140     setEditor : function(col, editor){
53141         this.config[col].editor = editor;
53142     }
53143 });
53144
53145 Roo.grid.ColumnModel.defaultRenderer = function(value){
53146         if(typeof value == "string" && value.length < 1){
53147             return "&#160;";
53148         }
53149         return value;
53150 };
53151
53152 // Alias for backwards compatibility
53153 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
53154 /*
53155  * Based on:
53156  * Ext JS Library 1.1.1
53157  * Copyright(c) 2006-2007, Ext JS, LLC.
53158  *
53159  * Originally Released Under LGPL - original licence link has changed is not relivant.
53160  *
53161  * Fork - LGPL
53162  * <script type="text/javascript">
53163  */
53164
53165 /**
53166  * @class Roo.grid.AbstractSelectionModel
53167  * @extends Roo.util.Observable
53168  * Abstract base class for grid SelectionModels.  It provides the interface that should be
53169  * implemented by descendant classes.  This class should not be directly instantiated.
53170  * @constructor
53171  */
53172 Roo.grid.AbstractSelectionModel = function(){
53173     this.locked = false;
53174     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
53175 };
53176
53177 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
53178     /** @ignore Called by the grid automatically. Do not call directly. */
53179     init : function(grid){
53180         this.grid = grid;
53181         this.initEvents();
53182     },
53183
53184     /**
53185      * Locks the selections.
53186      */
53187     lock : function(){
53188         this.locked = true;
53189     },
53190
53191     /**
53192      * Unlocks the selections.
53193      */
53194     unlock : function(){
53195         this.locked = false;
53196     },
53197
53198     /**
53199      * Returns true if the selections are locked.
53200      * @return {Boolean}
53201      */
53202     isLocked : function(){
53203         return this.locked;
53204     }
53205 });/*
53206  * Based on:
53207  * Ext JS Library 1.1.1
53208  * Copyright(c) 2006-2007, Ext JS, LLC.
53209  *
53210  * Originally Released Under LGPL - original licence link has changed is not relivant.
53211  *
53212  * Fork - LGPL
53213  * <script type="text/javascript">
53214  */
53215 /**
53216  * @extends Roo.grid.AbstractSelectionModel
53217  * @class Roo.grid.RowSelectionModel
53218  * The default SelectionModel used by {@link Roo.grid.Grid}.
53219  * It supports multiple selections and keyboard selection/navigation. 
53220  * @constructor
53221  * @param {Object} config
53222  */
53223 Roo.grid.RowSelectionModel = function(config){
53224     Roo.apply(this, config);
53225     this.selections = new Roo.util.MixedCollection(false, function(o){
53226         return o.id;
53227     });
53228
53229     this.last = false;
53230     this.lastActive = false;
53231
53232     this.addEvents({
53233         /**
53234              * @event selectionchange
53235              * Fires when the selection changes
53236              * @param {SelectionModel} this
53237              */
53238             "selectionchange" : true,
53239         /**
53240              * @event afterselectionchange
53241              * Fires after the selection changes (eg. by key press or clicking)
53242              * @param {SelectionModel} this
53243              */
53244             "afterselectionchange" : true,
53245         /**
53246              * @event beforerowselect
53247              * Fires when a row is selected being selected, return false to cancel.
53248              * @param {SelectionModel} this
53249              * @param {Number} rowIndex The selected index
53250              * @param {Boolean} keepExisting False if other selections will be cleared
53251              */
53252             "beforerowselect" : true,
53253         /**
53254              * @event rowselect
53255              * Fires when a row is selected.
53256              * @param {SelectionModel} this
53257              * @param {Number} rowIndex The selected index
53258              * @param {Roo.data.Record} r The record
53259              */
53260             "rowselect" : true,
53261         /**
53262              * @event rowdeselect
53263              * Fires when a row is deselected.
53264              * @param {SelectionModel} this
53265              * @param {Number} rowIndex The selected index
53266              */
53267         "rowdeselect" : true
53268     });
53269     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
53270     this.locked = false;
53271 };
53272
53273 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
53274     /**
53275      * @cfg {Boolean} singleSelect
53276      * True to allow selection of only one row at a time (defaults to false)
53277      */
53278     singleSelect : false,
53279
53280     // private
53281     initEvents : function(){
53282
53283         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
53284             this.grid.on("mousedown", this.handleMouseDown, this);
53285         }else{ // allow click to work like normal
53286             this.grid.on("rowclick", this.handleDragableRowClick, this);
53287         }
53288
53289         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
53290             "up" : function(e){
53291                 if(!e.shiftKey){
53292                     this.selectPrevious(e.shiftKey);
53293                 }else if(this.last !== false && this.lastActive !== false){
53294                     var last = this.last;
53295                     this.selectRange(this.last,  this.lastActive-1);
53296                     this.grid.getView().focusRow(this.lastActive);
53297                     if(last !== false){
53298                         this.last = last;
53299                     }
53300                 }else{
53301                     this.selectFirstRow();
53302                 }
53303                 this.fireEvent("afterselectionchange", this);
53304             },
53305             "down" : function(e){
53306                 if(!e.shiftKey){
53307                     this.selectNext(e.shiftKey);
53308                 }else if(this.last !== false && this.lastActive !== false){
53309                     var last = this.last;
53310                     this.selectRange(this.last,  this.lastActive+1);
53311                     this.grid.getView().focusRow(this.lastActive);
53312                     if(last !== false){
53313                         this.last = last;
53314                     }
53315                 }else{
53316                     this.selectFirstRow();
53317                 }
53318                 this.fireEvent("afterselectionchange", this);
53319             },
53320             scope: this
53321         });
53322
53323         var view = this.grid.view;
53324         view.on("refresh", this.onRefresh, this);
53325         view.on("rowupdated", this.onRowUpdated, this);
53326         view.on("rowremoved", this.onRemove, this);
53327     },
53328
53329     // private
53330     onRefresh : function(){
53331         var ds = this.grid.dataSource, i, v = this.grid.view;
53332         var s = this.selections;
53333         s.each(function(r){
53334             if((i = ds.indexOfId(r.id)) != -1){
53335                 v.onRowSelect(i);
53336             }else{
53337                 s.remove(r);
53338             }
53339         });
53340     },
53341
53342     // private
53343     onRemove : function(v, index, r){
53344         this.selections.remove(r);
53345     },
53346
53347     // private
53348     onRowUpdated : function(v, index, r){
53349         if(this.isSelected(r)){
53350             v.onRowSelect(index);
53351         }
53352     },
53353
53354     /**
53355      * Select records.
53356      * @param {Array} records The records to select
53357      * @param {Boolean} keepExisting (optional) True to keep existing selections
53358      */
53359     selectRecords : function(records, keepExisting){
53360         if(!keepExisting){
53361             this.clearSelections();
53362         }
53363         var ds = this.grid.dataSource;
53364         for(var i = 0, len = records.length; i < len; i++){
53365             this.selectRow(ds.indexOf(records[i]), true);
53366         }
53367     },
53368
53369     /**
53370      * Gets the number of selected rows.
53371      * @return {Number}
53372      */
53373     getCount : function(){
53374         return this.selections.length;
53375     },
53376
53377     /**
53378      * Selects the first row in the grid.
53379      */
53380     selectFirstRow : function(){
53381         this.selectRow(0);
53382     },
53383
53384     /**
53385      * Select the last row.
53386      * @param {Boolean} keepExisting (optional) True to keep existing selections
53387      */
53388     selectLastRow : function(keepExisting){
53389         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
53390     },
53391
53392     /**
53393      * Selects the row immediately following the last selected row.
53394      * @param {Boolean} keepExisting (optional) True to keep existing selections
53395      */
53396     selectNext : function(keepExisting){
53397         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
53398             this.selectRow(this.last+1, keepExisting);
53399             this.grid.getView().focusRow(this.last);
53400         }
53401     },
53402
53403     /**
53404      * Selects the row that precedes the last selected row.
53405      * @param {Boolean} keepExisting (optional) True to keep existing selections
53406      */
53407     selectPrevious : function(keepExisting){
53408         if(this.last){
53409             this.selectRow(this.last-1, keepExisting);
53410             this.grid.getView().focusRow(this.last);
53411         }
53412     },
53413
53414     /**
53415      * Returns the selected records
53416      * @return {Array} Array of selected records
53417      */
53418     getSelections : function(){
53419         return [].concat(this.selections.items);
53420     },
53421
53422     /**
53423      * Returns the first selected record.
53424      * @return {Record}
53425      */
53426     getSelected : function(){
53427         return this.selections.itemAt(0);
53428     },
53429
53430
53431     /**
53432      * Clears all selections.
53433      */
53434     clearSelections : function(fast){
53435         if(this.locked) return;
53436         if(fast !== true){
53437             var ds = this.grid.dataSource;
53438             var s = this.selections;
53439             s.each(function(r){
53440                 this.deselectRow(ds.indexOfId(r.id));
53441             }, this);
53442             s.clear();
53443         }else{
53444             this.selections.clear();
53445         }
53446         this.last = false;
53447     },
53448
53449
53450     /**
53451      * Selects all rows.
53452      */
53453     selectAll : function(){
53454         if(this.locked) return;
53455         this.selections.clear();
53456         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
53457             this.selectRow(i, true);
53458         }
53459     },
53460
53461     /**
53462      * Returns True if there is a selection.
53463      * @return {Boolean}
53464      */
53465     hasSelection : function(){
53466         return this.selections.length > 0;
53467     },
53468
53469     /**
53470      * Returns True if the specified row is selected.
53471      * @param {Number/Record} record The record or index of the record to check
53472      * @return {Boolean}
53473      */
53474     isSelected : function(index){
53475         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
53476         return (r && this.selections.key(r.id) ? true : false);
53477     },
53478
53479     /**
53480      * Returns True if the specified record id is selected.
53481      * @param {String} id The id of record to check
53482      * @return {Boolean}
53483      */
53484     isIdSelected : function(id){
53485         return (this.selections.key(id) ? true : false);
53486     },
53487
53488     // private
53489     handleMouseDown : function(e, t){
53490         var view = this.grid.getView(), rowIndex;
53491         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
53492             return;
53493         };
53494         if(e.shiftKey && this.last !== false){
53495             var last = this.last;
53496             this.selectRange(last, rowIndex, e.ctrlKey);
53497             this.last = last; // reset the last
53498             view.focusRow(rowIndex);
53499         }else{
53500             var isSelected = this.isSelected(rowIndex);
53501             if(e.button !== 0 && isSelected){
53502                 view.focusRow(rowIndex);
53503             }else if(e.ctrlKey && isSelected){
53504                 this.deselectRow(rowIndex);
53505             }else if(!isSelected){
53506                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
53507                 view.focusRow(rowIndex);
53508             }
53509         }
53510         this.fireEvent("afterselectionchange", this);
53511     },
53512     // private
53513     handleDragableRowClick :  function(grid, rowIndex, e) 
53514     {
53515         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
53516             this.selectRow(rowIndex, false);
53517             grid.view.focusRow(rowIndex);
53518              this.fireEvent("afterselectionchange", this);
53519         }
53520     },
53521     
53522     /**
53523      * Selects multiple rows.
53524      * @param {Array} rows Array of the indexes of the row to select
53525      * @param {Boolean} keepExisting (optional) True to keep existing selections
53526      */
53527     selectRows : function(rows, keepExisting){
53528         if(!keepExisting){
53529             this.clearSelections();
53530         }
53531         for(var i = 0, len = rows.length; i < len; i++){
53532             this.selectRow(rows[i], true);
53533         }
53534     },
53535
53536     /**
53537      * Selects a range of rows. All rows in between startRow and endRow are also selected.
53538      * @param {Number} startRow The index of the first row in the range
53539      * @param {Number} endRow The index of the last row in the range
53540      * @param {Boolean} keepExisting (optional) True to retain existing selections
53541      */
53542     selectRange : function(startRow, endRow, keepExisting){
53543         if(this.locked) return;
53544         if(!keepExisting){
53545             this.clearSelections();
53546         }
53547         if(startRow <= endRow){
53548             for(var i = startRow; i <= endRow; i++){
53549                 this.selectRow(i, true);
53550             }
53551         }else{
53552             for(var i = startRow; i >= endRow; i--){
53553                 this.selectRow(i, true);
53554             }
53555         }
53556     },
53557
53558     /**
53559      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
53560      * @param {Number} startRow The index of the first row in the range
53561      * @param {Number} endRow The index of the last row in the range
53562      */
53563     deselectRange : function(startRow, endRow, preventViewNotify){
53564         if(this.locked) return;
53565         for(var i = startRow; i <= endRow; i++){
53566             this.deselectRow(i, preventViewNotify);
53567         }
53568     },
53569
53570     /**
53571      * Selects a row.
53572      * @param {Number} row The index of the row to select
53573      * @param {Boolean} keepExisting (optional) True to keep existing selections
53574      */
53575     selectRow : function(index, keepExisting, preventViewNotify){
53576         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
53577         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
53578             if(!keepExisting || this.singleSelect){
53579                 this.clearSelections();
53580             }
53581             var r = this.grid.dataSource.getAt(index);
53582             this.selections.add(r);
53583             this.last = this.lastActive = index;
53584             if(!preventViewNotify){
53585                 this.grid.getView().onRowSelect(index);
53586             }
53587             this.fireEvent("rowselect", this, index, r);
53588             this.fireEvent("selectionchange", this);
53589         }
53590     },
53591
53592     /**
53593      * Deselects a row.
53594      * @param {Number} row The index of the row to deselect
53595      */
53596     deselectRow : function(index, preventViewNotify){
53597         if(this.locked) return;
53598         if(this.last == index){
53599             this.last = false;
53600         }
53601         if(this.lastActive == index){
53602             this.lastActive = false;
53603         }
53604         var r = this.grid.dataSource.getAt(index);
53605         this.selections.remove(r);
53606         if(!preventViewNotify){
53607             this.grid.getView().onRowDeselect(index);
53608         }
53609         this.fireEvent("rowdeselect", this, index);
53610         this.fireEvent("selectionchange", this);
53611     },
53612
53613     // private
53614     restoreLast : function(){
53615         if(this._last){
53616             this.last = this._last;
53617         }
53618     },
53619
53620     // private
53621     acceptsNav : function(row, col, cm){
53622         return !cm.isHidden(col) && cm.isCellEditable(col, row);
53623     },
53624
53625     // private
53626     onEditorKey : function(field, e){
53627         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
53628         if(k == e.TAB){
53629             e.stopEvent();
53630             ed.completeEdit();
53631             if(e.shiftKey){
53632                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
53633             }else{
53634                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
53635             }
53636         }else if(k == e.ENTER && !e.ctrlKey){
53637             e.stopEvent();
53638             ed.completeEdit();
53639             if(e.shiftKey){
53640                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
53641             }else{
53642                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
53643             }
53644         }else if(k == e.ESC){
53645             ed.cancelEdit();
53646         }
53647         if(newCell){
53648             g.startEditing(newCell[0], newCell[1]);
53649         }
53650     }
53651 });/*
53652  * Based on:
53653  * Ext JS Library 1.1.1
53654  * Copyright(c) 2006-2007, Ext JS, LLC.
53655  *
53656  * Originally Released Under LGPL - original licence link has changed is not relivant.
53657  *
53658  * Fork - LGPL
53659  * <script type="text/javascript">
53660  */
53661 /**
53662  * @class Roo.grid.CellSelectionModel
53663  * @extends Roo.grid.AbstractSelectionModel
53664  * This class provides the basic implementation for cell selection in a grid.
53665  * @constructor
53666  * @param {Object} config The object containing the configuration of this model.
53667  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
53668  */
53669 Roo.grid.CellSelectionModel = function(config){
53670     Roo.apply(this, config);
53671
53672     this.selection = null;
53673
53674     this.addEvents({
53675         /**
53676              * @event beforerowselect
53677              * Fires before a cell is selected.
53678              * @param {SelectionModel} this
53679              * @param {Number} rowIndex The selected row index
53680              * @param {Number} colIndex The selected cell index
53681              */
53682             "beforecellselect" : true,
53683         /**
53684              * @event cellselect
53685              * Fires when a cell is selected.
53686              * @param {SelectionModel} this
53687              * @param {Number} rowIndex The selected row index
53688              * @param {Number} colIndex The selected cell index
53689              */
53690             "cellselect" : true,
53691         /**
53692              * @event selectionchange
53693              * Fires when the active selection changes.
53694              * @param {SelectionModel} this
53695              * @param {Object} selection null for no selection or an object (o) with two properties
53696                 <ul>
53697                 <li>o.record: the record object for the row the selection is in</li>
53698                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
53699                 </ul>
53700              */
53701             "selectionchange" : true,
53702         /**
53703              * @event tabend
53704              * Fires when the tab (or enter) was pressed on the last editable cell
53705              * You can use this to trigger add new row.
53706              * @param {SelectionModel} this
53707              */
53708             "tabend" : true,
53709          /**
53710              * @event beforeeditnext
53711              * Fires before the next editable sell is made active
53712              * You can use this to skip to another cell or fire the tabend
53713              *    if you set cell to false
53714              * @param {Object} eventdata object : { cell : [ row, col ] } 
53715              */
53716             "beforeeditnext" : true
53717     });
53718     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
53719 };
53720
53721 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
53722     
53723     enter_is_tab: false,
53724
53725     /** @ignore */
53726     initEvents : function(){
53727         this.grid.on("mousedown", this.handleMouseDown, this);
53728         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
53729         var view = this.grid.view;
53730         view.on("refresh", this.onViewChange, this);
53731         view.on("rowupdated", this.onRowUpdated, this);
53732         view.on("beforerowremoved", this.clearSelections, this);
53733         view.on("beforerowsinserted", this.clearSelections, this);
53734         if(this.grid.isEditor){
53735             this.grid.on("beforeedit", this.beforeEdit,  this);
53736         }
53737     },
53738
53739         //private
53740     beforeEdit : function(e){
53741         this.select(e.row, e.column, false, true, e.record);
53742     },
53743
53744         //private
53745     onRowUpdated : function(v, index, r){
53746         if(this.selection && this.selection.record == r){
53747             v.onCellSelect(index, this.selection.cell[1]);
53748         }
53749     },
53750
53751         //private
53752     onViewChange : function(){
53753         this.clearSelections(true);
53754     },
53755
53756         /**
53757          * Returns the currently selected cell,.
53758          * @return {Array} The selected cell (row, column) or null if none selected.
53759          */
53760     getSelectedCell : function(){
53761         return this.selection ? this.selection.cell : null;
53762     },
53763
53764     /**
53765      * Clears all selections.
53766      * @param {Boolean} true to prevent the gridview from being notified about the change.
53767      */
53768     clearSelections : function(preventNotify){
53769         var s = this.selection;
53770         if(s){
53771             if(preventNotify !== true){
53772                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
53773             }
53774             this.selection = null;
53775             this.fireEvent("selectionchange", this, null);
53776         }
53777     },
53778
53779     /**
53780      * Returns true if there is a selection.
53781      * @return {Boolean}
53782      */
53783     hasSelection : function(){
53784         return this.selection ? true : false;
53785     },
53786
53787     /** @ignore */
53788     handleMouseDown : function(e, t){
53789         var v = this.grid.getView();
53790         if(this.isLocked()){
53791             return;
53792         };
53793         var row = v.findRowIndex(t);
53794         var cell = v.findCellIndex(t);
53795         if(row !== false && cell !== false){
53796             this.select(row, cell);
53797         }
53798     },
53799
53800     /**
53801      * Selects a cell.
53802      * @param {Number} rowIndex
53803      * @param {Number} collIndex
53804      */
53805     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
53806         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
53807             this.clearSelections();
53808             r = r || this.grid.dataSource.getAt(rowIndex);
53809             this.selection = {
53810                 record : r,
53811                 cell : [rowIndex, colIndex]
53812             };
53813             if(!preventViewNotify){
53814                 var v = this.grid.getView();
53815                 v.onCellSelect(rowIndex, colIndex);
53816                 if(preventFocus !== true){
53817                     v.focusCell(rowIndex, colIndex);
53818                 }
53819             }
53820             this.fireEvent("cellselect", this, rowIndex, colIndex);
53821             this.fireEvent("selectionchange", this, this.selection);
53822         }
53823     },
53824
53825         //private
53826     isSelectable : function(rowIndex, colIndex, cm){
53827         return !cm.isHidden(colIndex);
53828     },
53829
53830     /** @ignore */
53831     handleKeyDown : function(e){
53832         //Roo.log('Cell Sel Model handleKeyDown');
53833         if(!e.isNavKeyPress()){
53834             return;
53835         }
53836         var g = this.grid, s = this.selection;
53837         if(!s){
53838             e.stopEvent();
53839             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
53840             if(cell){
53841                 this.select(cell[0], cell[1]);
53842             }
53843             return;
53844         }
53845         var sm = this;
53846         var walk = function(row, col, step){
53847             return g.walkCells(row, col, step, sm.isSelectable,  sm);
53848         };
53849         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
53850         var newCell;
53851
53852       
53853
53854         switch(k){
53855             case e.TAB:
53856                 // handled by onEditorKey
53857                 if (g.isEditor && g.editing) {
53858                     return;
53859                 }
53860                 if(e.shiftKey) {
53861                     newCell = walk(r, c-1, -1);
53862                 } else {
53863                     newCell = walk(r, c+1, 1);
53864                 }
53865                 break;
53866             
53867             case e.DOWN:
53868                newCell = walk(r+1, c, 1);
53869                 break;
53870             
53871             case e.UP:
53872                 newCell = walk(r-1, c, -1);
53873                 break;
53874             
53875             case e.RIGHT:
53876                 newCell = walk(r, c+1, 1);
53877                 break;
53878             
53879             case e.LEFT:
53880                 newCell = walk(r, c-1, -1);
53881                 break;
53882             
53883             case e.ENTER:
53884                 
53885                 if(g.isEditor && !g.editing){
53886                    g.startEditing(r, c);
53887                    e.stopEvent();
53888                    return;
53889                 }
53890                 
53891                 
53892              break;
53893         };
53894         if(newCell){
53895             this.select(newCell[0], newCell[1]);
53896             e.stopEvent();
53897             
53898         }
53899     },
53900
53901     acceptsNav : function(row, col, cm){
53902         return !cm.isHidden(col) && cm.isCellEditable(col, row);
53903     },
53904     /**
53905      * Selects a cell.
53906      * @param {Number} field (not used) - as it's normally used as a listener
53907      * @param {Number} e - event - fake it by using
53908      *
53909      * var e = Roo.EventObjectImpl.prototype;
53910      * e.keyCode = e.TAB
53911      *
53912      * 
53913      */
53914     onEditorKey : function(field, e){
53915         
53916         var k = e.getKey(),
53917             newCell,
53918             g = this.grid,
53919             ed = g.activeEditor,
53920             forward = false;
53921         ///Roo.log('onEditorKey' + k);
53922         
53923         
53924         if (this.enter_is_tab && k == e.ENTER) {
53925             k = e.TAB;
53926         }
53927         
53928         if(k == e.TAB){
53929             if(e.shiftKey){
53930                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
53931             }else{
53932                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
53933                 forward = true;
53934             }
53935             
53936             e.stopEvent();
53937             
53938         } else if(k == e.ENTER &&  !e.ctrlKey){
53939             ed.completeEdit();
53940             e.stopEvent();
53941             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
53942         
53943                 } else if(k == e.ESC){
53944             ed.cancelEdit();
53945         }
53946                 
53947         if (newCell) {
53948             var ecall = { cell : newCell, forward : forward };
53949             this.fireEvent('beforeeditnext', ecall );
53950             newCell = ecall.cell;
53951                         forward = ecall.forward;
53952         }
53953                 
53954         if(newCell){
53955             //Roo.log('next cell after edit');
53956             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
53957         } else if (forward) {
53958             // tabbed past last
53959             this.fireEvent.defer(100, this, ['tabend',this]);
53960         }
53961     }
53962 });/*
53963  * Based on:
53964  * Ext JS Library 1.1.1
53965  * Copyright(c) 2006-2007, Ext JS, LLC.
53966  *
53967  * Originally Released Under LGPL - original licence link has changed is not relivant.
53968  *
53969  * Fork - LGPL
53970  * <script type="text/javascript">
53971  */
53972  
53973 /**
53974  * @class Roo.grid.EditorGrid
53975  * @extends Roo.grid.Grid
53976  * Class for creating and editable grid.
53977  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
53978  * The container MUST have some type of size defined for the grid to fill. The container will be 
53979  * automatically set to position relative if it isn't already.
53980  * @param {Object} dataSource The data model to bind to
53981  * @param {Object} colModel The column model with info about this grid's columns
53982  */
53983 Roo.grid.EditorGrid = function(container, config){
53984     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
53985     this.getGridEl().addClass("xedit-grid");
53986
53987     if(!this.selModel){
53988         this.selModel = new Roo.grid.CellSelectionModel();
53989     }
53990
53991     this.activeEditor = null;
53992
53993         this.addEvents({
53994             /**
53995              * @event beforeedit
53996              * Fires before cell editing is triggered. The edit event object has the following properties <br />
53997              * <ul style="padding:5px;padding-left:16px;">
53998              * <li>grid - This grid</li>
53999              * <li>record - The record being edited</li>
54000              * <li>field - The field name being edited</li>
54001              * <li>value - The value for the field being edited.</li>
54002              * <li>row - The grid row index</li>
54003              * <li>column - The grid column index</li>
54004              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
54005              * </ul>
54006              * @param {Object} e An edit event (see above for description)
54007              */
54008             "beforeedit" : true,
54009             /**
54010              * @event afteredit
54011              * Fires after a cell is edited. <br />
54012              * <ul style="padding:5px;padding-left:16px;">
54013              * <li>grid - This grid</li>
54014              * <li>record - The record being edited</li>
54015              * <li>field - The field name being edited</li>
54016              * <li>value - The value being set</li>
54017              * <li>originalValue - The original value for the field, before the edit.</li>
54018              * <li>row - The grid row index</li>
54019              * <li>column - The grid column index</li>
54020              * </ul>
54021              * @param {Object} e An edit event (see above for description)
54022              */
54023             "afteredit" : true,
54024             /**
54025              * @event validateedit
54026              * Fires after a cell is edited, but before the value is set in the record. 
54027          * You can use this to modify the value being set in the field, Return false
54028              * to cancel the change. The edit event object has the following properties <br />
54029              * <ul style="padding:5px;padding-left:16px;">
54030          * <li>editor - This editor</li>
54031              * <li>grid - This grid</li>
54032              * <li>record - The record being edited</li>
54033              * <li>field - The field name being edited</li>
54034              * <li>value - The value being set</li>
54035              * <li>originalValue - The original value for the field, before the edit.</li>
54036              * <li>row - The grid row index</li>
54037              * <li>column - The grid column index</li>
54038              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
54039              * </ul>
54040              * @param {Object} e An edit event (see above for description)
54041              */
54042             "validateedit" : true
54043         });
54044     this.on("bodyscroll", this.stopEditing,  this);
54045     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
54046 };
54047
54048 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
54049     /**
54050      * @cfg {Number} clicksToEdit
54051      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
54052      */
54053     clicksToEdit: 2,
54054
54055     // private
54056     isEditor : true,
54057     // private
54058     trackMouseOver: false, // causes very odd FF errors
54059
54060     onCellDblClick : function(g, row, col){
54061         this.startEditing(row, col);
54062     },
54063
54064     onEditComplete : function(ed, value, startValue){
54065         this.editing = false;
54066         this.activeEditor = null;
54067         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
54068         var r = ed.record;
54069         var field = this.colModel.getDataIndex(ed.col);
54070         var e = {
54071             grid: this,
54072             record: r,
54073             field: field,
54074             originalValue: startValue,
54075             value: value,
54076             row: ed.row,
54077             column: ed.col,
54078             cancel:false,
54079             editor: ed
54080         };
54081         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
54082         cell.show();
54083           
54084         if(String(value) !== String(startValue)){
54085             
54086             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
54087                 r.set(field, e.value);
54088                 // if we are dealing with a combo box..
54089                 // then we also set the 'name' colum to be the displayField
54090                 if (ed.field.displayField && ed.field.name) {
54091                     r.set(ed.field.name, ed.field.el.dom.value);
54092                 }
54093                 
54094                 delete e.cancel; //?? why!!!
54095                 this.fireEvent("afteredit", e);
54096             }
54097         } else {
54098             this.fireEvent("afteredit", e); // always fire it!
54099         }
54100         this.view.focusCell(ed.row, ed.col);
54101     },
54102
54103     /**
54104      * Starts editing the specified for the specified row/column
54105      * @param {Number} rowIndex
54106      * @param {Number} colIndex
54107      */
54108     startEditing : function(row, col){
54109         this.stopEditing();
54110         if(this.colModel.isCellEditable(col, row)){
54111             this.view.ensureVisible(row, col, true);
54112           
54113             var r = this.dataSource.getAt(row);
54114             var field = this.colModel.getDataIndex(col);
54115             var cell = Roo.get(this.view.getCell(row,col));
54116             var e = {
54117                 grid: this,
54118                 record: r,
54119                 field: field,
54120                 value: r.data[field],
54121                 row: row,
54122                 column: col,
54123                 cancel:false 
54124             };
54125             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
54126                 this.editing = true;
54127                 var ed = this.colModel.getCellEditor(col, row);
54128                 
54129                 if (!ed) {
54130                     return;
54131                 }
54132                 if(!ed.rendered){
54133                     ed.render(ed.parentEl || document.body);
54134                 }
54135                 ed.field.reset();
54136                
54137                 cell.hide();
54138                 
54139                 (function(){ // complex but required for focus issues in safari, ie and opera
54140                     ed.row = row;
54141                     ed.col = col;
54142                     ed.record = r;
54143                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
54144                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
54145                     this.activeEditor = ed;
54146                     var v = r.data[field];
54147                     ed.startEdit(this.view.getCell(row, col), v);
54148                     // combo's with 'displayField and name set
54149                     if (ed.field.displayField && ed.field.name) {
54150                         ed.field.el.dom.value = r.data[ed.field.name];
54151                     }
54152                     
54153                     
54154                 }).defer(50, this);
54155             }
54156         }
54157     },
54158         
54159     /**
54160      * Stops any active editing
54161      */
54162     stopEditing : function(){
54163         if(this.activeEditor){
54164             this.activeEditor.completeEdit();
54165         }
54166         this.activeEditor = null;
54167     },
54168         
54169          /**
54170      * Called to get grid's drag proxy text, by default returns this.ddText.
54171      * @return {String}
54172      */
54173     getDragDropText : function(){
54174         var count = this.selModel.getSelectedCell() ? 1 : 0;
54175         return String.format(this.ddText, count, count == 1 ? '' : 's');
54176     }
54177         
54178 });/*
54179  * Based on:
54180  * Ext JS Library 1.1.1
54181  * Copyright(c) 2006-2007, Ext JS, LLC.
54182  *
54183  * Originally Released Under LGPL - original licence link has changed is not relivant.
54184  *
54185  * Fork - LGPL
54186  * <script type="text/javascript">
54187  */
54188
54189 // private - not really -- you end up using it !
54190 // This is a support class used internally by the Grid components
54191
54192 /**
54193  * @class Roo.grid.GridEditor
54194  * @extends Roo.Editor
54195  * Class for creating and editable grid elements.
54196  * @param {Object} config any settings (must include field)
54197  */
54198 Roo.grid.GridEditor = function(field, config){
54199     if (!config && field.field) {
54200         config = field;
54201         field = Roo.factory(config.field, Roo.form);
54202     }
54203     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
54204     field.monitorTab = false;
54205 };
54206
54207 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
54208     
54209     /**
54210      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
54211      */
54212     
54213     alignment: "tl-tl",
54214     autoSize: "width",
54215     hideEl : false,
54216     cls: "x-small-editor x-grid-editor",
54217     shim:false,
54218     shadow:"frame"
54219 });/*
54220  * Based on:
54221  * Ext JS Library 1.1.1
54222  * Copyright(c) 2006-2007, Ext JS, LLC.
54223  *
54224  * Originally Released Under LGPL - original licence link has changed is not relivant.
54225  *
54226  * Fork - LGPL
54227  * <script type="text/javascript">
54228  */
54229   
54230
54231   
54232 Roo.grid.PropertyRecord = Roo.data.Record.create([
54233     {name:'name',type:'string'},  'value'
54234 ]);
54235
54236
54237 Roo.grid.PropertyStore = function(grid, source){
54238     this.grid = grid;
54239     this.store = new Roo.data.Store({
54240         recordType : Roo.grid.PropertyRecord
54241     });
54242     this.store.on('update', this.onUpdate,  this);
54243     if(source){
54244         this.setSource(source);
54245     }
54246     Roo.grid.PropertyStore.superclass.constructor.call(this);
54247 };
54248
54249
54250
54251 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
54252     setSource : function(o){
54253         this.source = o;
54254         this.store.removeAll();
54255         var data = [];
54256         for(var k in o){
54257             if(this.isEditableValue(o[k])){
54258                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
54259             }
54260         }
54261         this.store.loadRecords({records: data}, {}, true);
54262     },
54263
54264     onUpdate : function(ds, record, type){
54265         if(type == Roo.data.Record.EDIT){
54266             var v = record.data['value'];
54267             var oldValue = record.modified['value'];
54268             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
54269                 this.source[record.id] = v;
54270                 record.commit();
54271                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
54272             }else{
54273                 record.reject();
54274             }
54275         }
54276     },
54277
54278     getProperty : function(row){
54279        return this.store.getAt(row);
54280     },
54281
54282     isEditableValue: function(val){
54283         if(val && val instanceof Date){
54284             return true;
54285         }else if(typeof val == 'object' || typeof val == 'function'){
54286             return false;
54287         }
54288         return true;
54289     },
54290
54291     setValue : function(prop, value){
54292         this.source[prop] = value;
54293         this.store.getById(prop).set('value', value);
54294     },
54295
54296     getSource : function(){
54297         return this.source;
54298     }
54299 });
54300
54301 Roo.grid.PropertyColumnModel = function(grid, store){
54302     this.grid = grid;
54303     var g = Roo.grid;
54304     g.PropertyColumnModel.superclass.constructor.call(this, [
54305         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
54306         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
54307     ]);
54308     this.store = store;
54309     this.bselect = Roo.DomHelper.append(document.body, {
54310         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
54311             {tag: 'option', value: 'true', html: 'true'},
54312             {tag: 'option', value: 'false', html: 'false'}
54313         ]
54314     });
54315     Roo.id(this.bselect);
54316     var f = Roo.form;
54317     this.editors = {
54318         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
54319         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
54320         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
54321         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
54322         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
54323     };
54324     this.renderCellDelegate = this.renderCell.createDelegate(this);
54325     this.renderPropDelegate = this.renderProp.createDelegate(this);
54326 };
54327
54328 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
54329     
54330     
54331     nameText : 'Name',
54332     valueText : 'Value',
54333     
54334     dateFormat : 'm/j/Y',
54335     
54336     
54337     renderDate : function(dateVal){
54338         return dateVal.dateFormat(this.dateFormat);
54339     },
54340
54341     renderBool : function(bVal){
54342         return bVal ? 'true' : 'false';
54343     },
54344
54345     isCellEditable : function(colIndex, rowIndex){
54346         return colIndex == 1;
54347     },
54348
54349     getRenderer : function(col){
54350         return col == 1 ?
54351             this.renderCellDelegate : this.renderPropDelegate;
54352     },
54353
54354     renderProp : function(v){
54355         return this.getPropertyName(v);
54356     },
54357
54358     renderCell : function(val){
54359         var rv = val;
54360         if(val instanceof Date){
54361             rv = this.renderDate(val);
54362         }else if(typeof val == 'boolean'){
54363             rv = this.renderBool(val);
54364         }
54365         return Roo.util.Format.htmlEncode(rv);
54366     },
54367
54368     getPropertyName : function(name){
54369         var pn = this.grid.propertyNames;
54370         return pn && pn[name] ? pn[name] : name;
54371     },
54372
54373     getCellEditor : function(colIndex, rowIndex){
54374         var p = this.store.getProperty(rowIndex);
54375         var n = p.data['name'], val = p.data['value'];
54376         
54377         if(typeof(this.grid.customEditors[n]) == 'string'){
54378             return this.editors[this.grid.customEditors[n]];
54379         }
54380         if(typeof(this.grid.customEditors[n]) != 'undefined'){
54381             return this.grid.customEditors[n];
54382         }
54383         if(val instanceof Date){
54384             return this.editors['date'];
54385         }else if(typeof val == 'number'){
54386             return this.editors['number'];
54387         }else if(typeof val == 'boolean'){
54388             return this.editors['boolean'];
54389         }else{
54390             return this.editors['string'];
54391         }
54392     }
54393 });
54394
54395 /**
54396  * @class Roo.grid.PropertyGrid
54397  * @extends Roo.grid.EditorGrid
54398  * This class represents the  interface of a component based property grid control.
54399  * <br><br>Usage:<pre><code>
54400  var grid = new Roo.grid.PropertyGrid("my-container-id", {
54401       
54402  });
54403  // set any options
54404  grid.render();
54405  * </code></pre>
54406   
54407  * @constructor
54408  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54409  * The container MUST have some type of size defined for the grid to fill. The container will be
54410  * automatically set to position relative if it isn't already.
54411  * @param {Object} config A config object that sets properties on this grid.
54412  */
54413 Roo.grid.PropertyGrid = function(container, config){
54414     config = config || {};
54415     var store = new Roo.grid.PropertyStore(this);
54416     this.store = store;
54417     var cm = new Roo.grid.PropertyColumnModel(this, store);
54418     store.store.sort('name', 'ASC');
54419     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
54420         ds: store.store,
54421         cm: cm,
54422         enableColLock:false,
54423         enableColumnMove:false,
54424         stripeRows:false,
54425         trackMouseOver: false,
54426         clicksToEdit:1
54427     }, config));
54428     this.getGridEl().addClass('x-props-grid');
54429     this.lastEditRow = null;
54430     this.on('columnresize', this.onColumnResize, this);
54431     this.addEvents({
54432          /**
54433              * @event beforepropertychange
54434              * Fires before a property changes (return false to stop?)
54435              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
54436              * @param {String} id Record Id
54437              * @param {String} newval New Value
54438          * @param {String} oldval Old Value
54439              */
54440         "beforepropertychange": true,
54441         /**
54442              * @event propertychange
54443              * Fires after a property changes
54444              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
54445              * @param {String} id Record Id
54446              * @param {String} newval New Value
54447          * @param {String} oldval Old Value
54448              */
54449         "propertychange": true
54450     });
54451     this.customEditors = this.customEditors || {};
54452 };
54453 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
54454     
54455      /**
54456      * @cfg {Object} customEditors map of colnames=> custom editors.
54457      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
54458      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
54459      * false disables editing of the field.
54460          */
54461     
54462       /**
54463      * @cfg {Object} propertyNames map of property Names to their displayed value
54464          */
54465     
54466     render : function(){
54467         Roo.grid.PropertyGrid.superclass.render.call(this);
54468         this.autoSize.defer(100, this);
54469     },
54470
54471     autoSize : function(){
54472         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
54473         if(this.view){
54474             this.view.fitColumns();
54475         }
54476     },
54477
54478     onColumnResize : function(){
54479         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
54480         this.autoSize();
54481     },
54482     /**
54483      * Sets the data for the Grid
54484      * accepts a Key => Value object of all the elements avaiable.
54485      * @param {Object} data  to appear in grid.
54486      */
54487     setSource : function(source){
54488         this.store.setSource(source);
54489         //this.autoSize();
54490     },
54491     /**
54492      * Gets all the data from the grid.
54493      * @return {Object} data  data stored in grid
54494      */
54495     getSource : function(){
54496         return this.store.getSource();
54497     }
54498 });/*
54499  * Based on:
54500  * Ext JS Library 1.1.1
54501  * Copyright(c) 2006-2007, Ext JS, LLC.
54502  *
54503  * Originally Released Under LGPL - original licence link has changed is not relivant.
54504  *
54505  * Fork - LGPL
54506  * <script type="text/javascript">
54507  */
54508  
54509 /**
54510  * @class Roo.LoadMask
54511  * A simple utility class for generically masking elements while loading data.  If the element being masked has
54512  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
54513  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
54514  * element's UpdateManager load indicator and will be destroyed after the initial load.
54515  * @constructor
54516  * Create a new LoadMask
54517  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
54518  * @param {Object} config The config object
54519  */
54520 Roo.LoadMask = function(el, config){
54521     this.el = Roo.get(el);
54522     Roo.apply(this, config);
54523     if(this.store){
54524         this.store.on('beforeload', this.onBeforeLoad, this);
54525         this.store.on('load', this.onLoad, this);
54526         this.store.on('loadexception', this.onLoadException, this);
54527         this.removeMask = false;
54528     }else{
54529         var um = this.el.getUpdateManager();
54530         um.showLoadIndicator = false; // disable the default indicator
54531         um.on('beforeupdate', this.onBeforeLoad, this);
54532         um.on('update', this.onLoad, this);
54533         um.on('failure', this.onLoad, this);
54534         this.removeMask = true;
54535     }
54536 };
54537
54538 Roo.LoadMask.prototype = {
54539     /**
54540      * @cfg {Boolean} removeMask
54541      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
54542      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
54543      */
54544     /**
54545      * @cfg {String} msg
54546      * The text to display in a centered loading message box (defaults to 'Loading...')
54547      */
54548     msg : 'Loading...',
54549     /**
54550      * @cfg {String} msgCls
54551      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
54552      */
54553     msgCls : 'x-mask-loading',
54554
54555     /**
54556      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
54557      * @type Boolean
54558      */
54559     disabled: false,
54560
54561     /**
54562      * Disables the mask to prevent it from being displayed
54563      */
54564     disable : function(){
54565        this.disabled = true;
54566     },
54567
54568     /**
54569      * Enables the mask so that it can be displayed
54570      */
54571     enable : function(){
54572         this.disabled = false;
54573     },
54574     
54575     onLoadException : function()
54576     {
54577         Roo.log(arguments);
54578         
54579         if (typeof(arguments[3]) != 'undefined') {
54580             Roo.MessageBox.alert("Error loading",arguments[3]);
54581         } 
54582         /*
54583         try {
54584             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
54585                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
54586             }   
54587         } catch(e) {
54588             
54589         }
54590         */
54591     
54592         
54593         
54594         this.el.unmask(this.removeMask);
54595     },
54596     // private
54597     onLoad : function()
54598     {
54599         this.el.unmask(this.removeMask);
54600     },
54601
54602     // private
54603     onBeforeLoad : function(){
54604         if(!this.disabled){
54605             this.el.mask(this.msg, this.msgCls);
54606         }
54607     },
54608
54609     // private
54610     destroy : function(){
54611         if(this.store){
54612             this.store.un('beforeload', this.onBeforeLoad, this);
54613             this.store.un('load', this.onLoad, this);
54614             this.store.un('loadexception', this.onLoadException, this);
54615         }else{
54616             var um = this.el.getUpdateManager();
54617             um.un('beforeupdate', this.onBeforeLoad, this);
54618             um.un('update', this.onLoad, this);
54619             um.un('failure', this.onLoad, this);
54620         }
54621     }
54622 };/*
54623  * Based on:
54624  * Ext JS Library 1.1.1
54625  * Copyright(c) 2006-2007, Ext JS, LLC.
54626  *
54627  * Originally Released Under LGPL - original licence link has changed is not relivant.
54628  *
54629  * Fork - LGPL
54630  * <script type="text/javascript">
54631  */
54632
54633
54634 /**
54635  * @class Roo.XTemplate
54636  * @extends Roo.Template
54637  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
54638 <pre><code>
54639 var t = new Roo.XTemplate(
54640         '&lt;select name="{name}"&gt;',
54641                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
54642         '&lt;/select&gt;'
54643 );
54644  
54645 // then append, applying the master template values
54646  </code></pre>
54647  *
54648  * Supported features:
54649  *
54650  *  Tags:
54651
54652 <pre><code>
54653       {a_variable} - output encoded.
54654       {a_variable.format:("Y-m-d")} - call a method on the variable
54655       {a_variable:raw} - unencoded output
54656       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
54657       {a_variable:this.method_on_template(...)} - call a method on the template object.
54658  
54659 </code></pre>
54660  *  The tpl tag:
54661 <pre><code>
54662         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
54663         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
54664         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
54665         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
54666   
54667         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
54668         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
54669 </code></pre>
54670  *      
54671  */
54672 Roo.XTemplate = function()
54673 {
54674     Roo.XTemplate.superclass.constructor.apply(this, arguments);
54675     if (this.html) {
54676         this.compile();
54677     }
54678 };
54679
54680
54681 Roo.extend(Roo.XTemplate, Roo.Template, {
54682
54683     /**
54684      * The various sub templates
54685      */
54686     tpls : false,
54687     /**
54688      *
54689      * basic tag replacing syntax
54690      * WORD:WORD()
54691      *
54692      * // you can fake an object call by doing this
54693      *  x.t:(test,tesT) 
54694      * 
54695      */
54696     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
54697
54698     /**
54699      * compile the template
54700      *
54701      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
54702      *
54703      */
54704     compile: function()
54705     {
54706         var s = this.html;
54707      
54708         s = ['<tpl>', s, '</tpl>'].join('');
54709     
54710         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
54711             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
54712             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
54713             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
54714             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
54715             m,
54716             id     = 0,
54717             tpls   = [];
54718     
54719         while(true == !!(m = s.match(re))){
54720             var forMatch   = m[0].match(nameRe),
54721                 ifMatch   = m[0].match(ifRe),
54722                 execMatch   = m[0].match(execRe),
54723                 namedMatch   = m[0].match(namedRe),
54724                 
54725                 exp  = null, 
54726                 fn   = null,
54727                 exec = null,
54728                 name = forMatch && forMatch[1] ? forMatch[1] : '';
54729                 
54730             if (ifMatch) {
54731                 // if - puts fn into test..
54732                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
54733                 if(exp){
54734                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
54735                 }
54736             }
54737             
54738             if (execMatch) {
54739                 // exec - calls a function... returns empty if true is  returned.
54740                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
54741                 if(exp){
54742                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
54743                 }
54744             }
54745             
54746             
54747             if (name) {
54748                 // for = 
54749                 switch(name){
54750                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
54751                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
54752                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
54753                 }
54754             }
54755             var uid = namedMatch ? namedMatch[1] : id;
54756             
54757             
54758             tpls.push({
54759                 id:     namedMatch ? namedMatch[1] : id,
54760                 target: name,
54761                 exec:   exec,
54762                 test:   fn,
54763                 body:   m[1] || ''
54764             });
54765             if (namedMatch) {
54766                 s = s.replace(m[0], '');
54767             } else { 
54768                 s = s.replace(m[0], '{xtpl'+ id + '}');
54769             }
54770             ++id;
54771         }
54772         this.tpls = [];
54773         for(var i = tpls.length-1; i >= 0; --i){
54774             this.compileTpl(tpls[i]);
54775             this.tpls[tpls[i].id] = tpls[i];
54776         }
54777         this.master = tpls[tpls.length-1];
54778         return this;
54779     },
54780     /**
54781      * same as applyTemplate, except it's done to one of the subTemplates
54782      * when using named templates, you can do:
54783      *
54784      * var str = pl.applySubTemplate('your-name', values);
54785      *
54786      * 
54787      * @param {Number} id of the template
54788      * @param {Object} values to apply to template
54789      * @param {Object} parent (normaly the instance of this object)
54790      */
54791     applySubTemplate : function(id, values, parent)
54792     {
54793         
54794         
54795         var t = this.tpls[id];
54796         
54797         
54798         try { 
54799             if(t.test && !t.test.call(this, values, parent)){
54800                 return '';
54801             }
54802         } catch(e) {
54803             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
54804             Roo.log(e.toString());
54805             Roo.log(t.test);
54806             return ''
54807         }
54808         try { 
54809             
54810             if(t.exec && t.exec.call(this, values, parent)){
54811                 return '';
54812             }
54813         } catch(e) {
54814             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
54815             Roo.log(e.toString());
54816             Roo.log(t.exec);
54817             return ''
54818         }
54819         try {
54820             var vs = t.target ? t.target.call(this, values, parent) : values;
54821             parent = t.target ? values : parent;
54822             if(t.target && vs instanceof Array){
54823                 var buf = [];
54824                 for(var i = 0, len = vs.length; i < len; i++){
54825                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
54826                 }
54827                 return buf.join('');
54828             }
54829             return t.compiled.call(this, vs, parent);
54830         } catch (e) {
54831             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
54832             Roo.log(e.toString());
54833             Roo.log(t.compiled);
54834             return '';
54835         }
54836     },
54837
54838     compileTpl : function(tpl)
54839     {
54840         var fm = Roo.util.Format;
54841         var useF = this.disableFormats !== true;
54842         var sep = Roo.isGecko ? "+" : ",";
54843         var undef = function(str) {
54844             Roo.log("Property not found :"  + str);
54845             return '';
54846         };
54847         
54848         var fn = function(m, name, format, args)
54849         {
54850             //Roo.log(arguments);
54851             args = args ? args.replace(/\\'/g,"'") : args;
54852             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
54853             if (typeof(format) == 'undefined') {
54854                 format= 'htmlEncode';
54855             }
54856             if (format == 'raw' ) {
54857                 format = false;
54858             }
54859             
54860             if(name.substr(0, 4) == 'xtpl'){
54861                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
54862             }
54863             
54864             // build an array of options to determine if value is undefined..
54865             
54866             // basically get 'xxxx.yyyy' then do
54867             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
54868             //    (function () { Roo.log("Property not found"); return ''; })() :
54869             //    ......
54870             
54871             var udef_ar = [];
54872             var lookfor = '';
54873             Roo.each(name.split('.'), function(st) {
54874                 lookfor += (lookfor.length ? '.': '') + st;
54875                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
54876             });
54877             
54878             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
54879             
54880             
54881             if(format && useF){
54882                 
54883                 args = args ? ',' + args : "";
54884                  
54885                 if(format.substr(0, 5) != "this."){
54886                     format = "fm." + format + '(';
54887                 }else{
54888                     format = 'this.call("'+ format.substr(5) + '", ';
54889                     args = ", values";
54890                 }
54891                 
54892                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
54893             }
54894              
54895             if (args.length) {
54896                 // called with xxyx.yuu:(test,test)
54897                 // change to ()
54898                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
54899             }
54900             // raw.. - :raw modifier..
54901             return "'"+ sep + udef_st  + name + ")"+sep+"'";
54902             
54903         };
54904         var body;
54905         // branched to use + in gecko and [].join() in others
54906         if(Roo.isGecko){
54907             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
54908                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
54909                     "';};};";
54910         }else{
54911             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
54912             body.push(tpl.body.replace(/(\r\n|\n)/g,
54913                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
54914             body.push("'].join('');};};");
54915             body = body.join('');
54916         }
54917         
54918         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
54919        
54920         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
54921         eval(body);
54922         
54923         return this;
54924     },
54925
54926     applyTemplate : function(values){
54927         return this.master.compiled.call(this, values, {});
54928         //var s = this.subs;
54929     },
54930
54931     apply : function(){
54932         return this.applyTemplate.apply(this, arguments);
54933     }
54934
54935  });
54936
54937 Roo.XTemplate.from = function(el){
54938     el = Roo.getDom(el);
54939     return new Roo.XTemplate(el.value || el.innerHTML);
54940 };/*
54941  * Original code for Roojs - LGPL
54942  * <script type="text/javascript">
54943  */
54944  
54945 /**
54946  * @class Roo.XComponent
54947  * A delayed Element creator...
54948  * Or a way to group chunks of interface together.
54949  * 
54950  * Mypart.xyx = new Roo.XComponent({
54951
54952     parent : 'Mypart.xyz', // empty == document.element.!!
54953     order : '001',
54954     name : 'xxxx'
54955     region : 'xxxx'
54956     disabled : function() {} 
54957      
54958     tree : function() { // return an tree of xtype declared components
54959         var MODULE = this;
54960         return 
54961         {
54962             xtype : 'NestedLayoutPanel',
54963             // technicall
54964         }
54965      ]
54966  *})
54967  *
54968  *
54969  * It can be used to build a big heiracy, with parent etc.
54970  * or you can just use this to render a single compoent to a dom element
54971  * MYPART.render(Roo.Element | String(id) | dom_element )
54972  * 
54973  * @extends Roo.util.Observable
54974  * @constructor
54975  * @param cfg {Object} configuration of component
54976  * 
54977  */
54978 Roo.XComponent = function(cfg) {
54979     Roo.apply(this, cfg);
54980     this.addEvents({ 
54981         /**
54982              * @event built
54983              * Fires when this the componnt is built
54984              * @param {Roo.XComponent} c the component
54985              */
54986         'built' : true
54987         
54988     });
54989     this.region = this.region || 'center'; // default..
54990     Roo.XComponent.register(this);
54991     this.modules = false;
54992     this.el = false; // where the layout goes..
54993     
54994     
54995 }
54996 Roo.extend(Roo.XComponent, Roo.util.Observable, {
54997     /**
54998      * @property el
54999      * The created element (with Roo.factory())
55000      * @type {Roo.Layout}
55001      */
55002     el  : false,
55003     
55004     /**
55005      * @property el
55006      * for BC  - use el in new code
55007      * @type {Roo.Layout}
55008      */
55009     panel : false,
55010     
55011     /**
55012      * @property layout
55013      * for BC  - use el in new code
55014      * @type {Roo.Layout}
55015      */
55016     layout : false,
55017     
55018      /**
55019      * @cfg {Function|boolean} disabled
55020      * If this module is disabled by some rule, return true from the funtion
55021      */
55022     disabled : false,
55023     
55024     /**
55025      * @cfg {String} parent 
55026      * Name of parent element which it get xtype added to..
55027      */
55028     parent: false,
55029     
55030     /**
55031      * @cfg {String} order
55032      * Used to set the order in which elements are created (usefull for multiple tabs)
55033      */
55034     
55035     order : false,
55036     /**
55037      * @cfg {String} name
55038      * String to display while loading.
55039      */
55040     name : false,
55041     /**
55042      * @cfg {String} region
55043      * Region to render component to (defaults to center)
55044      */
55045     region : 'center',
55046     
55047     /**
55048      * @cfg {Array} items
55049      * A single item array - the first element is the root of the tree..
55050      * It's done this way to stay compatible with the Xtype system...
55051      */
55052     items : false,
55053     
55054     /**
55055      * @property _tree
55056      * The method that retuns the tree of parts that make up this compoennt 
55057      * @type {function}
55058      */
55059     _tree  : false,
55060     
55061      /**
55062      * render
55063      * render element to dom or tree
55064      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
55065      */
55066     
55067     render : function(el)
55068     {
55069         
55070         el = el || false;
55071         var hp = this.parent ? 1 : 0;
55072         
55073         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
55074             // if parent is a '#.....' string, then let's use that..
55075             var ename = this.parent.substr(1)
55076             this.parent = false;
55077             el = Roo.get(ename);
55078             if (!el) {
55079                 Roo.log("Warning - element can not be found :#" + ename );
55080                 return;
55081             }
55082         }
55083         
55084         
55085         if (!this.parent) {
55086             
55087             el = el ? Roo.get(el) : false;      
55088             
55089             // it's a top level one..
55090             this.parent =  {
55091                 el : new Roo.BorderLayout(el || document.body, {
55092                 
55093                      center: {
55094                          titlebar: false,
55095                          autoScroll:false,
55096                          closeOnTab: true,
55097                          tabPosition: 'top',
55098                           //resizeTabs: true,
55099                          alwaysShowTabs: el && hp? false :  true,
55100                          hideTabs: el || !hp ? true :  false,
55101                          minTabWidth: 140
55102                      }
55103                  })
55104             }
55105         }
55106         
55107                 if (!this.parent.el) {
55108                         // probably an old style ctor, which has been disabled.
55109                         return;
55110                         
55111                 }
55112                 // The 'tree' method is  '_tree now' 
55113             
55114         var tree = this._tree ? this._tree() : this.tree();
55115         tree.region = tree.region || this.region;
55116         this.el = this.parent.el.addxtype(tree);
55117         this.fireEvent('built', this);
55118         
55119         this.panel = this.el;
55120         this.layout = this.panel.layout;
55121                 this.parentLayout = this.parent.layout  || false;  
55122          
55123     }
55124     
55125 });
55126
55127 Roo.apply(Roo.XComponent, {
55128     /**
55129      * @property  hideProgress
55130      * true to disable the building progress bar.. usefull on single page renders.
55131      * @type Boolean
55132      */
55133     hideProgress : false,
55134     /**
55135      * @property  buildCompleted
55136      * True when the builder has completed building the interface.
55137      * @type Boolean
55138      */
55139     buildCompleted : false,
55140      
55141     /**
55142      * @property  topModule
55143      * the upper most module - uses document.element as it's constructor.
55144      * @type Object
55145      */
55146      
55147     topModule  : false,
55148       
55149     /**
55150      * @property  modules
55151      * array of modules to be created by registration system.
55152      * @type {Array} of Roo.XComponent
55153      */
55154     
55155     modules : [],
55156     /**
55157      * @property  elmodules
55158      * array of modules to be created by which use #ID 
55159      * @type {Array} of Roo.XComponent
55160      */
55161      
55162     elmodules : [],
55163
55164     
55165     /**
55166      * Register components to be built later.
55167      *
55168      * This solves the following issues
55169      * - Building is not done on page load, but after an authentication process has occured.
55170      * - Interface elements are registered on page load
55171      * - Parent Interface elements may not be loaded before child, so this handles that..
55172      * 
55173      *
55174      * example:
55175      * 
55176      * MyApp.register({
55177           order : '000001',
55178           module : 'Pman.Tab.projectMgr',
55179           region : 'center',
55180           parent : 'Pman.layout',
55181           disabled : false,  // or use a function..
55182         })
55183      
55184      * * @param {Object} details about module
55185      */
55186     register : function(obj) {
55187                 
55188         Roo.XComponent.event.fireEvent('register', obj);
55189         switch(typeof(obj.disabled) ) {
55190                 
55191             case 'undefined':
55192                 break;
55193             
55194             case 'function':
55195                 if ( obj.disabled() ) {
55196                         return;
55197                 }
55198                 break;
55199             
55200             default:
55201                 if (obj.disabled) {
55202                         return;
55203                 }
55204                 break;
55205         }
55206                 
55207         this.modules.push(obj);
55208          
55209     },
55210     /**
55211      * convert a string to an object..
55212      * eg. 'AAA.BBB' -> finds AAA.BBB
55213
55214      */
55215     
55216     toObject : function(str)
55217     {
55218         if (!str || typeof(str) == 'object') {
55219             return str;
55220         }
55221         if (str.substring(0,1) == '#') {
55222             return str;
55223         }
55224
55225         var ar = str.split('.');
55226         var rt, o;
55227         rt = ar.shift();
55228             /** eval:var:o */
55229         try {
55230             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
55231         } catch (e) {
55232             throw "Module not found : " + str;
55233         }
55234         
55235         if (o === false) {
55236             throw "Module not found : " + str;
55237         }
55238         Roo.each(ar, function(e) {
55239             if (typeof(o[e]) == 'undefined') {
55240                 throw "Module not found : " + str;
55241             }
55242             o = o[e];
55243         });
55244         
55245         return o;
55246         
55247     },
55248     
55249     
55250     /**
55251      * move modules into their correct place in the tree..
55252      * 
55253      */
55254     preBuild : function ()
55255     {
55256         var _t = this;
55257         Roo.each(this.modules , function (obj)
55258         {
55259             Roo.XComponent.event.fireEvent('beforebuild', obj);
55260             
55261             var opar = obj.parent;
55262             try { 
55263                 obj.parent = this.toObject(opar);
55264             } catch(e) {
55265                 Roo.log("parent:toObject failed: " + e.toString());
55266                 return;
55267             }
55268             
55269             if (!obj.parent) {
55270                 Roo.debug && Roo.log("GOT top level module");
55271                 Roo.debug && Roo.log(obj);
55272                 obj.modules = new Roo.util.MixedCollection(false, 
55273                     function(o) { return o.order + '' }
55274                 );
55275                 this.topModule = obj;
55276                 return;
55277             }
55278                         // parent is a string (usually a dom element name..)
55279             if (typeof(obj.parent) == 'string') {
55280                 this.elmodules.push(obj);
55281                 return;
55282             }
55283             if (obj.parent.constructor != Roo.XComponent) {
55284                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
55285             }
55286             if (!obj.parent.modules) {
55287                 obj.parent.modules = new Roo.util.MixedCollection(false, 
55288                     function(o) { return o.order + '' }
55289                 );
55290             }
55291             if (obj.parent.disabled) {
55292                 obj.disabled = true;
55293             }
55294             obj.parent.modules.add(obj);
55295         }, this);
55296     },
55297     
55298      /**
55299      * make a list of modules to build.
55300      * @return {Array} list of modules. 
55301      */ 
55302     
55303     buildOrder : function()
55304     {
55305         var _this = this;
55306         var cmp = function(a,b) {   
55307             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
55308         };
55309         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
55310             throw "No top level modules to build";
55311         }
55312         
55313         // make a flat list in order of modules to build.
55314         var mods = this.topModule ? [ this.topModule ] : [];
55315                 
55316         
55317         // elmodules (is a list of DOM based modules )
55318         Roo.each(this.elmodules, function(e) {
55319             mods.push(e);
55320             if (!this.topModule &&
55321                 typeof(e.parent) == 'string' &&
55322                 e.parent.substring(0,1) == '#' &&
55323                 Roo.get(e.parent.substr(1))
55324                ) {
55325                 
55326                 _this.topModule = e;
55327             }
55328             
55329         });
55330
55331         
55332         // add modules to their parents..
55333         var addMod = function(m) {
55334             Roo.debug && Roo.log("build Order: add: " + m.name);
55335                 
55336             mods.push(m);
55337             if (m.modules && !m.disabled) {
55338                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
55339                 m.modules.keySort('ASC',  cmp );
55340                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
55341     
55342                 m.modules.each(addMod);
55343             } else {
55344                 Roo.debug && Roo.log("build Order: no child modules");
55345             }
55346             // not sure if this is used any more..
55347             if (m.finalize) {
55348                 m.finalize.name = m.name + " (clean up) ";
55349                 mods.push(m.finalize);
55350             }
55351             
55352         }
55353         if (this.topModule && this.topModule.modules) { 
55354             this.topModule.modules.keySort('ASC',  cmp );
55355             this.topModule.modules.each(addMod);
55356         } 
55357         return mods;
55358     },
55359     
55360      /**
55361      * Build the registered modules.
55362      * @param {Object} parent element.
55363      * @param {Function} optional method to call after module has been added.
55364      * 
55365      */ 
55366    
55367     build : function() 
55368     {
55369         
55370         this.preBuild();
55371         var mods = this.buildOrder();
55372       
55373         //this.allmods = mods;
55374         //Roo.debug && Roo.log(mods);
55375         //return;
55376         if (!mods.length) { // should not happen
55377             throw "NO modules!!!";
55378         }
55379         
55380         
55381         var msg = "Building Interface...";
55382         // flash it up as modal - so we store the mask!?
55383         if (!this.hideProgress) {
55384             Roo.MessageBox.show({ title: 'loading' });
55385             Roo.MessageBox.show({
55386                title: "Please wait...",
55387                msg: msg,
55388                width:450,
55389                progress:true,
55390                closable:false,
55391                modal: false
55392               
55393             });
55394         }
55395         var total = mods.length;
55396         
55397         var _this = this;
55398         var progressRun = function() {
55399             if (!mods.length) {
55400                 Roo.debug && Roo.log('hide?');
55401                 if (!this.hideProgress) {
55402                     Roo.MessageBox.hide();
55403                 }
55404                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
55405                 
55406                 // THE END...
55407                 return false;   
55408             }
55409             
55410             var m = mods.shift();
55411             
55412             
55413             Roo.debug && Roo.log(m);
55414             // not sure if this is supported any more.. - modules that are are just function
55415             if (typeof(m) == 'function') { 
55416                 m.call(this);
55417                 return progressRun.defer(10, _this);
55418             } 
55419             
55420             
55421             msg = "Building Interface " + (total  - mods.length) + 
55422                     " of " + total + 
55423                     (m.name ? (' - ' + m.name) : '');
55424                         Roo.debug && Roo.log(msg);
55425             if (!this.hideProgress) { 
55426                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
55427             }
55428             
55429          
55430             // is the module disabled?
55431             var disabled = (typeof(m.disabled) == 'function') ?
55432                 m.disabled.call(m.module.disabled) : m.disabled;    
55433             
55434             
55435             if (disabled) {
55436                 return progressRun(); // we do not update the display!
55437             }
55438             
55439             // now build 
55440             
55441                         
55442                         
55443             m.render();
55444             // it's 10 on top level, and 1 on others??? why...
55445             return progressRun.defer(10, _this);
55446              
55447         }
55448         progressRun.defer(1, _this);
55449      
55450         
55451         
55452     },
55453         
55454         
55455         /**
55456          * Event Object.
55457          *
55458          *
55459          */
55460         event: false, 
55461     /**
55462          * wrapper for event.on - aliased later..  
55463          * Typically use to register a event handler for register:
55464          *
55465          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
55466          *
55467          */
55468     on : false
55469    
55470     
55471     
55472 });
55473
55474 Roo.XComponent.event = new Roo.util.Observable({
55475                 events : { 
55476                         /**
55477                          * @event register
55478                          * Fires when an Component is registered,
55479                          * set the disable property on the Component to stop registration.
55480                          * @param {Roo.XComponent} c the component being registerd.
55481                          * 
55482                          */
55483                         'register' : true,
55484             /**
55485                          * @event beforebuild
55486                          * Fires before each Component is built
55487                          * can be used to apply permissions.
55488                          * @param {Roo.XComponent} c the component being registerd.
55489                          * 
55490                          */
55491                         'beforebuild' : true,
55492                         /**
55493                          * @event buildcomplete
55494                          * Fires on the top level element when all elements have been built
55495                          * @param {Roo.XComponent} the top level component.
55496                          */
55497                         'buildcomplete' : true
55498                         
55499                 }
55500 });
55501
55502 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
55503