roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         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         isTouch = 'ontouchstart' in document.documentElement;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * 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.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * 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]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
650 /*
651  * Based on:
652  * Ext JS Library 1.1.1
653  * Copyright(c) 2006-2007, Ext JS, LLC.
654  *
655  * Originally Released Under LGPL - original licence link has changed is not relivant.
656  *
657  * Fork - LGPL
658  * <script type="text/javascript">
659  */
660
661 (function() {    
662     // wrappedn so fnCleanup is not in global scope...
663     if(Roo.isIE) {
664         function fnCleanUp() {
665             var p = Function.prototype;
666             delete p.createSequence;
667             delete p.defer;
668             delete p.createDelegate;
669             delete p.createCallback;
670             delete p.createInterceptor;
671
672             window.detachEvent("onunload", fnCleanUp);
673         }
674         window.attachEvent("onunload", fnCleanUp);
675     }
676 })();
677
678
679 /**
680  * @class Function
681  * These functions are available on every Function object (any JavaScript function).
682  */
683 Roo.apply(Function.prototype, {
684      /**
685      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
686      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
687      * Will create a function that is bound to those 2 args.
688      * @return {Function} The new function
689     */
690     createCallback : function(/*args...*/){
691         // make args available, in function below
692         var args = arguments;
693         var method = this;
694         return function() {
695             return method.apply(window, args);
696         };
697     },
698
699     /**
700      * Creates a delegate (callback) that sets the scope to obj.
701      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
702      * Will create a function that is automatically scoped to this.
703      * @param {Object} obj (optional) The object for which the scope is set
704      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
705      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
706      *                                             if a number the args are inserted at the specified position
707      * @return {Function} The new function
708      */
709     createDelegate : function(obj, args, appendArgs){
710         var method = this;
711         return function() {
712             var callArgs = args || arguments;
713             if(appendArgs === true){
714                 callArgs = Array.prototype.slice.call(arguments, 0);
715                 callArgs = callArgs.concat(args);
716             }else if(typeof appendArgs == "number"){
717                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
718                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
719                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
720             }
721             return method.apply(obj || window, callArgs);
722         };
723     },
724
725     /**
726      * Calls this function after the number of millseconds specified.
727      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
728      * @param {Object} obj (optional) The object for which the scope is set
729      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
730      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
731      *                                             if a number the args are inserted at the specified position
732      * @return {Number} The timeout id that can be used with clearTimeout
733      */
734     defer : function(millis, obj, args, appendArgs){
735         var fn = this.createDelegate(obj, args, appendArgs);
736         if(millis){
737             return setTimeout(fn, millis);
738         }
739         fn();
740         return 0;
741     },
742     /**
743      * Create a combined function call sequence of the original function + the passed function.
744      * The resulting function returns the results of the original function.
745      * The passed fcn is called with the parameters of the original function
746      * @param {Function} fcn The function to sequence
747      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
748      * @return {Function} The new function
749      */
750     createSequence : function(fcn, scope){
751         if(typeof fcn != "function"){
752             return this;
753         }
754         var method = this;
755         return function() {
756             var retval = method.apply(this || window, arguments);
757             fcn.apply(scope || this || window, arguments);
758             return retval;
759         };
760     },
761
762     /**
763      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function.
766      * @addon
767      * @param {Function} fcn The function to call before the original
768      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
769      * @return {Function} The new function
770      */
771     createInterceptor : function(fcn, scope){
772         if(typeof fcn != "function"){
773             return this;
774         }
775         var method = this;
776         return function() {
777             fcn.target = this;
778             fcn.method = method;
779             if(fcn.apply(scope || this || window, arguments) === false){
780                 return;
781             }
782             return method.apply(this || window, arguments);
783         };
784     }
785 });
786 /*
787  * Based on:
788  * Ext JS Library 1.1.1
789  * Copyright(c) 2006-2007, Ext JS, LLC.
790  *
791  * Originally Released Under LGPL - original licence link has changed is not relivant.
792  *
793  * Fork - LGPL
794  * <script type="text/javascript">
795  */
796
797 Roo.applyIf(String, {
798     
799     /** @scope String */
800     
801     /**
802      * Escapes the passed string for ' and \
803      * @param {String} string The string to escape
804      * @return {String} The escaped string
805      * @static
806      */
807     escape : function(string) {
808         return string.replace(/('|\\)/g, "\\$1");
809     },
810
811     /**
812      * Pads the left side of a string with a specified character.  This is especially useful
813      * for normalizing number and date strings.  Example usage:
814      * <pre><code>
815 var s = String.leftPad('123', 5, '0');
816 // s now contains the string: '00123'
817 </code></pre>
818      * @param {String} string The original string
819      * @param {Number} size The total length of the output string
820      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
821      * @return {String} The padded string
822      * @static
823      */
824     leftPad : function (val, size, ch) {
825         var result = new String(val);
826         if(ch === null || ch === undefined || ch === '') {
827             ch = " ";
828         }
829         while (result.length < size) {
830             result = ch + result;
831         }
832         return result;
833     },
834
835     /**
836      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
837      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
838      * <pre><code>
839 var cls = 'my-class', text = 'Some text';
840 var s = String.format('<div class="{0}">{1}</div>', cls, text);
841 // s now contains the string: '<div class="my-class">Some text</div>'
842 </code></pre>
843      * @param {String} string The tokenized string to be formatted
844      * @param {String} value1 The value to replace token {0}
845      * @param {String} value2 Etc...
846      * @return {String} The formatted string
847      * @static
848      */
849     format : function(format){
850         var args = Array.prototype.slice.call(arguments, 1);
851         return format.replace(/\{(\d+)\}/g, function(m, i){
852             return Roo.util.Format.htmlEncode(args[i]);
853         });
854     }
855 });
856
857 /**
858  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
859  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
860  * they are already different, the first value passed in is returned.  Note that this method returns the new value
861  * but does not change the current string.
862  * <pre><code>
863 // alternate sort directions
864 sort = sort.toggle('ASC', 'DESC');
865
866 // instead of conditional logic:
867 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
868 </code></pre>
869  * @param {String} value The value to compare to the current string
870  * @param {String} other The new value to use if the string already equals the first value passed in
871  * @return {String} The new value
872  */
873  
874 String.prototype.toggle = function(value, other){
875     return this == value ? other : value;
876 };/*
877  * Based on:
878  * Ext JS Library 1.1.1
879  * Copyright(c) 2006-2007, Ext JS, LLC.
880  *
881  * Originally Released Under LGPL - original licence link has changed is not relivant.
882  *
883  * Fork - LGPL
884  * <script type="text/javascript">
885  */
886
887  /**
888  * @class Number
889  */
890 Roo.applyIf(Number.prototype, {
891     /**
892      * Checks whether or not the current number is within a desired range.  If the number is already within the
893      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
894      * exceeded.  Note that this method returns the constrained value but does not change the current number.
895      * @param {Number} min The minimum number in the range
896      * @param {Number} max The maximum number in the range
897      * @return {Number} The constrained value if outside the range, otherwise the current value
898      */
899     constrain : function(min, max){
900         return Math.min(Math.max(this, min), max);
901     }
902 });/*
903  * Based on:
904  * Ext JS Library 1.1.1
905  * Copyright(c) 2006-2007, Ext JS, LLC.
906  *
907  * Originally Released Under LGPL - original licence link has changed is not relivant.
908  *
909  * Fork - LGPL
910  * <script type="text/javascript">
911  */
912  /**
913  * @class Array
914  */
915 Roo.applyIf(Array.prototype, {
916     /**
917      * Checks whether or not the specified object exists in the array.
918      * @param {Object} o The object to check for
919      * @return {Number} The index of o in the array (or -1 if it is not found)
920      */
921     indexOf : function(o){
922        for (var i = 0, len = this.length; i < len; i++){
923               if(this[i] == o) return i;
924        }
925            return -1;
926     },
927
928     /**
929      * Removes the specified object from the array.  If the object is not found nothing happens.
930      * @param {Object} o The object to remove
931      */
932     remove : function(o){
933        var index = this.indexOf(o);
934        if(index != -1){
935            this.splice(index, 1);
936        }
937     },
938     /**
939      * Map (JS 1.6 compatibility)
940      * @param {Function} function  to call
941      */
942     map : function(fun )
943     {
944         var len = this.length >>> 0;
945         if (typeof fun != "function")
946             throw new TypeError();
947
948         var res = new Array(len);
949         var thisp = arguments[1];
950         for (var i = 0; i < len; i++)
951         {
952             if (i in this)
953                 res[i] = fun.call(thisp, this[i], i, this);
954         }
955
956         return res;
957     }
958     
959 });
960
961
962  /*
963  * Based on:
964  * Ext JS Library 1.1.1
965  * Copyright(c) 2006-2007, Ext JS, LLC.
966  *
967  * Originally Released Under LGPL - original licence link has changed is not relivant.
968  *
969  * Fork - LGPL
970  * <script type="text/javascript">
971  */
972
973 /**
974  * @class Date
975  *
976  * The date parsing and format syntax is a subset of
977  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
978  * supported will provide results equivalent to their PHP versions.
979  *
980  * Following is the list of all currently supported formats:
981  *<pre>
982 Sample date:
983 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
984
985 Format  Output      Description
986 ------  ----------  --------------------------------------------------------------
987   d      10         Day of the month, 2 digits with leading zeros
988   D      Wed        A textual representation of a day, three letters
989   j      10         Day of the month without leading zeros
990   l      Wednesday  A full textual representation of the day of the week
991   S      th         English ordinal day of month suffix, 2 chars (use with j)
992   w      3          Numeric representation of the day of the week
993   z      9          The julian date, or day of the year (0-365)
994   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
995   F      January    A full textual representation of the month
996   m      01         Numeric representation of a month, with leading zeros
997   M      Jan        Month name abbreviation, three letters
998   n      1          Numeric representation of a month, without leading zeros
999   t      31         Number of days in the given month
1000   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1001   Y      2007       A full numeric representation of a year, 4 digits
1002   y      07         A two digit representation of a year
1003   a      pm         Lowercase Ante meridiem and Post meridiem
1004   A      PM         Uppercase Ante meridiem and Post meridiem
1005   g      3          12-hour format of an hour without leading zeros
1006   G      15         24-hour format of an hour without leading zeros
1007   h      03         12-hour format of an hour with leading zeros
1008   H      15         24-hour format of an hour with leading zeros
1009   i      05         Minutes with leading zeros
1010   s      01         Seconds, with leading zeros
1011   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1012   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1013   T      CST        Timezone setting of the machine running the code
1014   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1015 </pre>
1016  *
1017  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1018  * <pre><code>
1019 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1020 document.write(dt.format('Y-m-d'));                         //2007-01-10
1021 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1022 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
1023  </code></pre>
1024  *
1025  * Here are some standard date/time patterns that you might find helpful.  They
1026  * are not part of the source of Date.js, but to use them you can simply copy this
1027  * block of code into any script that is included after Date.js and they will also become
1028  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1029  * <pre><code>
1030 Date.patterns = {
1031     ISO8601Long:"Y-m-d H:i:s",
1032     ISO8601Short:"Y-m-d",
1033     ShortDate: "n/j/Y",
1034     LongDate: "l, F d, Y",
1035     FullDateTime: "l, F d, Y g:i:s A",
1036     MonthDay: "F d",
1037     ShortTime: "g:i A",
1038     LongTime: "g:i:s A",
1039     SortableDateTime: "Y-m-d\\TH:i:s",
1040     UniversalSortableDateTime: "Y-m-d H:i:sO",
1041     YearMonth: "F, Y"
1042 };
1043 </code></pre>
1044  *
1045  * Example usage:
1046  * <pre><code>
1047 var dt = new Date();
1048 document.write(dt.format(Date.patterns.ShortDate));
1049  </code></pre>
1050  */
1051
1052 /*
1053  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1054  * They generate precompiled functions from date formats instead of parsing and
1055  * processing the pattern every time you format a date.  These functions are available
1056  * on every Date object (any javascript function).
1057  *
1058  * The original article and download are here:
1059  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1060  *
1061  */
1062  
1063  
1064  // was in core
1065 /**
1066  Returns the number of milliseconds between this date and date
1067  @param {Date} date (optional) Defaults to now
1068  @return {Number} The diff in milliseconds
1069  @member Date getElapsed
1070  */
1071 Date.prototype.getElapsed = function(date) {
1072         return Math.abs((date || new Date()).getTime()-this.getTime());
1073 };
1074 // was in date file..
1075
1076
1077 // private
1078 Date.parseFunctions = {count:0};
1079 // private
1080 Date.parseRegexes = [];
1081 // private
1082 Date.formatFunctions = {count:0};
1083
1084 // private
1085 Date.prototype.dateFormat = function(format) {
1086     if (Date.formatFunctions[format] == null) {
1087         Date.createNewFormat(format);
1088     }
1089     var func = Date.formatFunctions[format];
1090     return this[func]();
1091 };
1092
1093
1094 /**
1095  * Formats a date given the supplied format string
1096  * @param {String} format The format string
1097  * @return {String} The formatted date
1098  * @method
1099  */
1100 Date.prototype.format = Date.prototype.dateFormat;
1101
1102 // private
1103 Date.createNewFormat = function(format) {
1104     var funcName = "format" + Date.formatFunctions.count++;
1105     Date.formatFunctions[format] = funcName;
1106     var code = "Date.prototype." + funcName + " = function(){return ";
1107     var special = false;
1108     var ch = '';
1109     for (var i = 0; i < format.length; ++i) {
1110         ch = format.charAt(i);
1111         if (!special && ch == "\\") {
1112             special = true;
1113         }
1114         else if (special) {
1115             special = false;
1116             code += "'" + String.escape(ch) + "' + ";
1117         }
1118         else {
1119             code += Date.getFormatCode(ch);
1120         }
1121     }
1122     /** eval:var:zzzzzzzzzzzzz */
1123     eval(code.substring(0, code.length - 3) + ";}");
1124 };
1125
1126 // private
1127 Date.getFormatCode = function(character) {
1128     switch (character) {
1129     case "d":
1130         return "String.leftPad(this.getDate(), 2, '0') + ";
1131     case "D":
1132         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1133     case "j":
1134         return "this.getDate() + ";
1135     case "l":
1136         return "Date.dayNames[this.getDay()] + ";
1137     case "S":
1138         return "this.getSuffix() + ";
1139     case "w":
1140         return "this.getDay() + ";
1141     case "z":
1142         return "this.getDayOfYear() + ";
1143     case "W":
1144         return "this.getWeekOfYear() + ";
1145     case "F":
1146         return "Date.monthNames[this.getMonth()] + ";
1147     case "m":
1148         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1149     case "M":
1150         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1151     case "n":
1152         return "(this.getMonth() + 1) + ";
1153     case "t":
1154         return "this.getDaysInMonth() + ";
1155     case "L":
1156         return "(this.isLeapYear() ? 1 : 0) + ";
1157     case "Y":
1158         return "this.getFullYear() + ";
1159     case "y":
1160         return "('' + this.getFullYear()).substring(2, 4) + ";
1161     case "a":
1162         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1163     case "A":
1164         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1165     case "g":
1166         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1167     case "G":
1168         return "this.getHours() + ";
1169     case "h":
1170         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1171     case "H":
1172         return "String.leftPad(this.getHours(), 2, '0') + ";
1173     case "i":
1174         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1175     case "s":
1176         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1177     case "O":
1178         return "this.getGMTOffset() + ";
1179     case "P":
1180         return "this.getGMTColonOffset() + ";
1181     case "T":
1182         return "this.getTimezone() + ";
1183     case "Z":
1184         return "(this.getTimezoneOffset() * -60) + ";
1185     default:
1186         return "'" + String.escape(character) + "' + ";
1187     }
1188 };
1189
1190 /**
1191  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1192  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1193  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1194  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1195  * string or the parse operation will fail.
1196  * Example Usage:
1197 <pre><code>
1198 //dt = Fri May 25 2007 (current date)
1199 var dt = new Date();
1200
1201 //dt = Thu May 25 2006 (today's month/day in 2006)
1202 dt = Date.parseDate("2006", "Y");
1203
1204 //dt = Sun Jan 15 2006 (all date parts specified)
1205 dt = Date.parseDate("2006-1-15", "Y-m-d");
1206
1207 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1208 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1209 </code></pre>
1210  * @param {String} input The unparsed date as a string
1211  * @param {String} format The format the date is in
1212  * @return {Date} The parsed date
1213  * @static
1214  */
1215 Date.parseDate = function(input, format) {
1216     if (Date.parseFunctions[format] == null) {
1217         Date.createParser(format);
1218     }
1219     var func = Date.parseFunctions[format];
1220     return Date[func](input);
1221 };
1222 /**
1223  * @private
1224  */
1225 Date.createParser = function(format) {
1226     var funcName = "parse" + Date.parseFunctions.count++;
1227     var regexNum = Date.parseRegexes.length;
1228     var currentGroup = 1;
1229     Date.parseFunctions[format] = funcName;
1230
1231     var code = "Date." + funcName + " = function(input){\n"
1232         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1233         + "var d = new Date();\n"
1234         + "y = d.getFullYear();\n"
1235         + "m = d.getMonth();\n"
1236         + "d = d.getDate();\n"
1237         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1238         + "if (results && results.length > 0) {";
1239     var regex = "";
1240
1241     var special = false;
1242     var ch = '';
1243     for (var i = 0; i < format.length; ++i) {
1244         ch = format.charAt(i);
1245         if (!special && ch == "\\") {
1246             special = true;
1247         }
1248         else if (special) {
1249             special = false;
1250             regex += String.escape(ch);
1251         }
1252         else {
1253             var obj = Date.formatCodeToRegex(ch, currentGroup);
1254             currentGroup += obj.g;
1255             regex += obj.s;
1256             if (obj.g && obj.c) {
1257                 code += obj.c;
1258             }
1259         }
1260     }
1261
1262     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i, s);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1265         + "{v = new Date(y, m, d, h, i);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1267         + "{v = new Date(y, m, d, h);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1269         + "{v = new Date(y, m, d);}\n"
1270         + "else if (y >= 0 && m >= 0)\n"
1271         + "{v = new Date(y, m);}\n"
1272         + "else if (y >= 0)\n"
1273         + "{v = new Date(y);}\n"
1274         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1275         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1276         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1277         + ";}";
1278
1279     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1280     /** eval:var:zzzzzzzzzzzzz */
1281     eval(code);
1282 };
1283
1284 // private
1285 Date.formatCodeToRegex = function(character, currentGroup) {
1286     switch (character) {
1287     case "D":
1288         return {g:0,
1289         c:null,
1290         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1291     case "j":
1292         return {g:1,
1293             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1294             s:"(\\d{1,2})"}; // day of month without leading zeroes
1295     case "d":
1296         return {g:1,
1297             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{2})"}; // day of month with leading zeroes
1299     case "l":
1300         return {g:0,
1301             c:null,
1302             s:"(?:" + Date.dayNames.join("|") + ")"};
1303     case "S":
1304         return {g:0,
1305             c:null,
1306             s:"(?:st|nd|rd|th)"};
1307     case "w":
1308         return {g:0,
1309             c:null,
1310             s:"\\d"};
1311     case "z":
1312         return {g:0,
1313             c:null,
1314             s:"(?:\\d{1,3})"};
1315     case "W":
1316         return {g:0,
1317             c:null,
1318             s:"(?:\\d{2})"};
1319     case "F":
1320         return {g:1,
1321             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1322             s:"(" + Date.monthNames.join("|") + ")"};
1323     case "M":
1324         return {g:1,
1325             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1326             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1327     case "n":
1328         return {g:1,
1329             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1330             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1331     case "m":
1332         return {g:1,
1333             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1334             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1335     case "t":
1336         return {g:0,
1337             c:null,
1338             s:"\\d{1,2}"};
1339     case "L":
1340         return {g:0,
1341             c:null,
1342             s:"(?:1|0)"};
1343     case "Y":
1344         return {g:1,
1345             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1346             s:"(\\d{4})"};
1347     case "y":
1348         return {g:1,
1349             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1350                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1351             s:"(\\d{1,2})"};
1352     case "a":
1353         return {g:1,
1354             c:"if (results[" + currentGroup + "] == 'am') {\n"
1355                 + "if (h == 12) { h = 0; }\n"
1356                 + "} else { if (h < 12) { h += 12; }}",
1357             s:"(am|pm)"};
1358     case "A":
1359         return {g:1,
1360             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1361                 + "if (h == 12) { h = 0; }\n"
1362                 + "} else { if (h < 12) { h += 12; }}",
1363             s:"(AM|PM)"};
1364     case "g":
1365     case "G":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1369     case "h":
1370     case "H":
1371         return {g:1,
1372             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1373             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1374     case "i":
1375         return {g:1,
1376             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1377             s:"(\\d{2})"};
1378     case "s":
1379         return {g:1,
1380             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{2})"};
1382     case "O":
1383         return {g:1,
1384             c:[
1385                 "o = results[", currentGroup, "];\n",
1386                 "var sn = o.substring(0,1);\n", // get + / - sign
1387                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1388                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1389                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1390                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1391             ].join(""),
1392             s:"([+\-]\\d{2,4})"};
1393     
1394     
1395     case "P":
1396         return {g:1,
1397                 c:[
1398                    "o = results[", currentGroup, "];\n",
1399                    "var sn = o.substring(0,1);\n",
1400                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1401                    "var mn = o.substring(4,6) % 60;\n",
1402                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1403                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1404             ].join(""),
1405             s:"([+\-]\\d{4})"};
1406     case "T":
1407         return {g:0,
1408             c:null,
1409             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1410     case "Z":
1411         return {g:1,
1412             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1413                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1414             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1415     default:
1416         return {g:0,
1417             c:null,
1418             s:String.escape(character)};
1419     }
1420 };
1421
1422 /**
1423  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1424  * @return {String} The abbreviated timezone name (e.g. 'CST')
1425  */
1426 Date.prototype.getTimezone = function() {
1427     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1428 };
1429
1430 /**
1431  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1432  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1433  */
1434 Date.prototype.getGMTOffset = function() {
1435     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1436         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1437         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1438 };
1439
1440 /**
1441  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1442  * @return {String} 2-characters representing hours and 2-characters representing minutes
1443  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1444  */
1445 Date.prototype.getGMTColonOffset = function() {
1446         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1447                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1448                 + ":"
1449                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1450 }
1451
1452 /**
1453  * Get the numeric day number of the year, adjusted for leap year.
1454  * @return {Number} 0 through 364 (365 in leap years)
1455  */
1456 Date.prototype.getDayOfYear = function() {
1457     var num = 0;
1458     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1459     for (var i = 0; i < this.getMonth(); ++i) {
1460         num += Date.daysInMonth[i];
1461     }
1462     return num + this.getDate() - 1;
1463 };
1464
1465 /**
1466  * Get the string representation of the numeric week number of the year
1467  * (equivalent to the format specifier 'W').
1468  * @return {String} '00' through '52'
1469  */
1470 Date.prototype.getWeekOfYear = function() {
1471     // Skip to Thursday of this week
1472     var now = this.getDayOfYear() + (4 - this.getDay());
1473     // Find the first Thursday of the year
1474     var jan1 = new Date(this.getFullYear(), 0, 1);
1475     var then = (7 - jan1.getDay() + 4);
1476     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1477 };
1478
1479 /**
1480  * Whether or not the current date is in a leap year.
1481  * @return {Boolean} True if the current date is in a leap year, else false
1482  */
1483 Date.prototype.isLeapYear = function() {
1484     var year = this.getFullYear();
1485     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1486 };
1487
1488 /**
1489  * Get the first day of the current month, adjusted for leap year.  The returned value
1490  * is the numeric day index within the week (0-6) which can be used in conjunction with
1491  * the {@link #monthNames} array to retrieve the textual day name.
1492  * Example:
1493  *<pre><code>
1494 var dt = new Date('1/10/2007');
1495 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1496 </code></pre>
1497  * @return {Number} The day number (0-6)
1498  */
1499 Date.prototype.getFirstDayOfMonth = function() {
1500     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1501     return (day < 0) ? (day + 7) : day;
1502 };
1503
1504 /**
1505  * Get the last day of the current month, adjusted for leap year.  The returned value
1506  * is the numeric day index within the week (0-6) which can be used in conjunction with
1507  * the {@link #monthNames} array to retrieve the textual day name.
1508  * Example:
1509  *<pre><code>
1510 var dt = new Date('1/10/2007');
1511 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1512 </code></pre>
1513  * @return {Number} The day number (0-6)
1514  */
1515 Date.prototype.getLastDayOfMonth = function() {
1516     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1517     return (day < 0) ? (day + 7) : day;
1518 };
1519
1520
1521 /**
1522  * Get the first date of this date's month
1523  * @return {Date}
1524  */
1525 Date.prototype.getFirstDateOfMonth = function() {
1526     return new Date(this.getFullYear(), this.getMonth(), 1);
1527 };
1528
1529 /**
1530  * Get the last date of this date's month
1531  * @return {Date}
1532  */
1533 Date.prototype.getLastDateOfMonth = function() {
1534     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1535 };
1536 /**
1537  * Get the number of days in the current month, adjusted for leap year.
1538  * @return {Number} The number of days in the month
1539  */
1540 Date.prototype.getDaysInMonth = function() {
1541     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1542     return Date.daysInMonth[this.getMonth()];
1543 };
1544
1545 /**
1546  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1547  * @return {String} 'st, 'nd', 'rd' or 'th'
1548  */
1549 Date.prototype.getSuffix = function() {
1550     switch (this.getDate()) {
1551         case 1:
1552         case 21:
1553         case 31:
1554             return "st";
1555         case 2:
1556         case 22:
1557             return "nd";
1558         case 3:
1559         case 23:
1560             return "rd";
1561         default:
1562             return "th";
1563     }
1564 };
1565
1566 // private
1567 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1568
1569 /**
1570  * An array of textual month names.
1571  * Override these values for international dates, for example...
1572  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1573  * @type Array
1574  * @static
1575  */
1576 Date.monthNames =
1577    ["January",
1578     "February",
1579     "March",
1580     "April",
1581     "May",
1582     "June",
1583     "July",
1584     "August",
1585     "September",
1586     "October",
1587     "November",
1588     "December"];
1589
1590 /**
1591  * An array of textual day names.
1592  * Override these values for international dates, for example...
1593  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1594  * @type Array
1595  * @static
1596  */
1597 Date.dayNames =
1598    ["Sunday",
1599     "Monday",
1600     "Tuesday",
1601     "Wednesday",
1602     "Thursday",
1603     "Friday",
1604     "Saturday"];
1605
1606 // private
1607 Date.y2kYear = 50;
1608 // private
1609 Date.monthNumbers = {
1610     Jan:0,
1611     Feb:1,
1612     Mar:2,
1613     Apr:3,
1614     May:4,
1615     Jun:5,
1616     Jul:6,
1617     Aug:7,
1618     Sep:8,
1619     Oct:9,
1620     Nov:10,
1621     Dec:11};
1622
1623 /**
1624  * Creates and returns a new Date instance with the exact same date value as the called instance.
1625  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1626  * variable will also be changed.  When the intention is to create a new variable that will not
1627  * modify the original instance, you should create a clone.
1628  *
1629  * Example of correctly cloning a date:
1630  * <pre><code>
1631 //wrong way:
1632 var orig = new Date('10/1/2006');
1633 var copy = orig;
1634 copy.setDate(5);
1635 document.write(orig);  //returns 'Thu Oct 05 2006'!
1636
1637 //correct way:
1638 var orig = new Date('10/1/2006');
1639 var copy = orig.clone();
1640 copy.setDate(5);
1641 document.write(orig);  //returns 'Thu Oct 01 2006'
1642 </code></pre>
1643  * @return {Date} The new Date instance
1644  */
1645 Date.prototype.clone = function() {
1646         return new Date(this.getTime());
1647 };
1648
1649 /**
1650  * Clears any time information from this date
1651  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1652  @return {Date} this or the clone
1653  */
1654 Date.prototype.clearTime = function(clone){
1655     if(clone){
1656         return this.clone().clearTime();
1657     }
1658     this.setHours(0);
1659     this.setMinutes(0);
1660     this.setSeconds(0);
1661     this.setMilliseconds(0);
1662     return this;
1663 };
1664
1665 // private
1666 // safari setMonth is broken
1667 if(Roo.isSafari){
1668     Date.brokenSetMonth = Date.prototype.setMonth;
1669         Date.prototype.setMonth = function(num){
1670                 if(num <= -1){
1671                         var n = Math.ceil(-num);
1672                         var back_year = Math.ceil(n/12);
1673                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1674                         this.setFullYear(this.getFullYear() - back_year);
1675                         return Date.brokenSetMonth.call(this, month);
1676                 } else {
1677                         return Date.brokenSetMonth.apply(this, arguments);
1678                 }
1679         };
1680 }
1681
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.MILLI = "ms";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.SECOND = "s";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.MINUTE = "mi";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.HOUR = "h";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.DAY = "d";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MONTH = "mo";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.YEAR = "y";
1710
1711 /**
1712  * Provides a convenient method of performing basic date arithmetic.  This method
1713  * does not modify the Date instance being called - it creates and returns
1714  * a new Date instance containing the resulting date value.
1715  *
1716  * Examples:
1717  * <pre><code>
1718 //Basic usage:
1719 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1720 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1721
1722 //Negative values will subtract correctly:
1723 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1724 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1725
1726 //You can even chain several calls together in one line!
1727 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1728 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1729  </code></pre>
1730  *
1731  * @param {String} interval   A valid date interval enum value
1732  * @param {Number} value      The amount to add to the current date
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.add = function(interval, value){
1736   var d = this.clone();
1737   if (!interval || value === 0) return d;
1738   switch(interval.toLowerCase()){
1739     case Date.MILLI:
1740       d.setMilliseconds(this.getMilliseconds() + value);
1741       break;
1742     case Date.SECOND:
1743       d.setSeconds(this.getSeconds() + value);
1744       break;
1745     case Date.MINUTE:
1746       d.setMinutes(this.getMinutes() + value);
1747       break;
1748     case Date.HOUR:
1749       d.setHours(this.getHours() + value);
1750       break;
1751     case Date.DAY:
1752       d.setDate(this.getDate() + value);
1753       break;
1754     case Date.MONTH:
1755       var day = this.getDate();
1756       if(day > 28){
1757           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1758       }
1759       d.setDate(day);
1760       d.setMonth(this.getMonth() + value);
1761       break;
1762     case Date.YEAR:
1763       d.setFullYear(this.getFullYear() + value);
1764       break;
1765   }
1766   return d;
1767 };
1768 /*
1769  * Based on:
1770  * Ext JS Library 1.1.1
1771  * Copyright(c) 2006-2007, Ext JS, LLC.
1772  *
1773  * Originally Released Under LGPL - original licence link has changed is not relivant.
1774  *
1775  * Fork - LGPL
1776  * <script type="text/javascript">
1777  */
1778
1779 /**
1780  * @class Roo.lib.Dom
1781  * @static
1782  * 
1783  * Dom utils (from YIU afaik)
1784  * 
1785  **/
1786 Roo.lib.Dom = {
1787     /**
1788      * Get the view width
1789      * @param {Boolean} full True will get the full document, otherwise it's the view width
1790      * @return {Number} The width
1791      */
1792      
1793     getViewWidth : function(full) {
1794         return full ? this.getDocumentWidth() : this.getViewportWidth();
1795     },
1796     /**
1797      * Get the view height
1798      * @param {Boolean} full True will get the full document, otherwise it's the view height
1799      * @return {Number} The height
1800      */
1801     getViewHeight : function(full) {
1802         return full ? this.getDocumentHeight() : this.getViewportHeight();
1803     },
1804
1805     getDocumentHeight: function() {
1806         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1807         return Math.max(scrollHeight, this.getViewportHeight());
1808     },
1809
1810     getDocumentWidth: function() {
1811         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1812         return Math.max(scrollWidth, this.getViewportWidth());
1813     },
1814
1815     getViewportHeight: function() {
1816         var height = self.innerHeight;
1817         var mode = document.compatMode;
1818
1819         if ((mode || Roo.isIE) && !Roo.isOpera) {
1820             height = (mode == "CSS1Compat") ?
1821                      document.documentElement.clientHeight :
1822                      document.body.clientHeight;
1823         }
1824
1825         return height;
1826     },
1827
1828     getViewportWidth: function() {
1829         var width = self.innerWidth;
1830         var mode = document.compatMode;
1831
1832         if (mode || Roo.isIE) {
1833             width = (mode == "CSS1Compat") ?
1834                     document.documentElement.clientWidth :
1835                     document.body.clientWidth;
1836         }
1837         return width;
1838     },
1839
1840     isAncestor : function(p, c) {
1841         p = Roo.getDom(p);
1842         c = Roo.getDom(c);
1843         if (!p || !c) {
1844             return false;
1845         }
1846
1847         if (p.contains && !Roo.isSafari) {
1848             return p.contains(c);
1849         } else if (p.compareDocumentPosition) {
1850             return !!(p.compareDocumentPosition(c) & 16);
1851         } else {
1852             var parent = c.parentNode;
1853             while (parent) {
1854                 if (parent == p) {
1855                     return true;
1856                 }
1857                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1858                     return false;
1859                 }
1860                 parent = parent.parentNode;
1861             }
1862             return false;
1863         }
1864     },
1865
1866     getRegion : function(el) {
1867         return Roo.lib.Region.getRegion(el);
1868     },
1869
1870     getY : function(el) {
1871         return this.getXY(el)[1];
1872     },
1873
1874     getX : function(el) {
1875         return this.getXY(el)[0];
1876     },
1877
1878     getXY : function(el) {
1879         var p, pe, b, scroll, bd = document.body;
1880         el = Roo.getDom(el);
1881         var fly = Roo.lib.AnimBase.fly;
1882         if (el.getBoundingClientRect) {
1883             b = el.getBoundingClientRect();
1884             scroll = fly(document).getScroll();
1885             return [b.left + scroll.left, b.top + scroll.top];
1886         }
1887         var x = 0, y = 0;
1888
1889         p = el;
1890
1891         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1892
1893         while (p) {
1894
1895             x += p.offsetLeft;
1896             y += p.offsetTop;
1897
1898             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1899                 hasAbsolute = true;
1900             }
1901
1902             if (Roo.isGecko) {
1903                 pe = fly(p);
1904
1905                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1906                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1907
1908
1909                 x += bl;
1910                 y += bt;
1911
1912
1913                 if (p != el && pe.getStyle('overflow') != 'visible') {
1914                     x += bl;
1915                     y += bt;
1916                 }
1917             }
1918             p = p.offsetParent;
1919         }
1920
1921         if (Roo.isSafari && hasAbsolute) {
1922             x -= bd.offsetLeft;
1923             y -= bd.offsetTop;
1924         }
1925
1926         if (Roo.isGecko && !hasAbsolute) {
1927             var dbd = fly(bd);
1928             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1929             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1930         }
1931
1932         p = el.parentNode;
1933         while (p && p != bd) {
1934             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1935                 x -= p.scrollLeft;
1936                 y -= p.scrollTop;
1937             }
1938             p = p.parentNode;
1939         }
1940         return [x, y];
1941     },
1942  
1943   
1944
1945
1946     setXY : function(el, xy) {
1947         el = Roo.fly(el, '_setXY');
1948         el.position();
1949         var pts = el.translatePoints(xy);
1950         if (xy[0] !== false) {
1951             el.dom.style.left = pts.left + "px";
1952         }
1953         if (xy[1] !== false) {
1954             el.dom.style.top = pts.top + "px";
1955         }
1956     },
1957
1958     setX : function(el, x) {
1959         this.setXY(el, [x, false]);
1960     },
1961
1962     setY : function(el, y) {
1963         this.setXY(el, [false, y]);
1964     }
1965 };
1966 /*
1967  * Portions of this file are based on pieces of Yahoo User Interface Library
1968  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1969  * YUI licensed under the BSD License:
1970  * http://developer.yahoo.net/yui/license.txt
1971  * <script type="text/javascript">
1972  *
1973  */
1974
1975 Roo.lib.Event = function() {
1976     var loadComplete = false;
1977     var listeners = [];
1978     var unloadListeners = [];
1979     var retryCount = 0;
1980     var onAvailStack = [];
1981     var counter = 0;
1982     var lastError = null;
1983
1984     return {
1985         POLL_RETRYS: 200,
1986         POLL_INTERVAL: 20,
1987         EL: 0,
1988         TYPE: 1,
1989         FN: 2,
1990         WFN: 3,
1991         OBJ: 3,
1992         ADJ_SCOPE: 4,
1993         _interval: null,
1994
1995         startInterval: function() {
1996             if (!this._interval) {
1997                 var self = this;
1998                 var callback = function() {
1999                     self._tryPreloadAttach();
2000                 };
2001                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2002
2003             }
2004         },
2005
2006         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2007             onAvailStack.push({ id:         p_id,
2008                 fn:         p_fn,
2009                 obj:        p_obj,
2010                 override:   p_override,
2011                 checkReady: false    });
2012
2013             retryCount = this.POLL_RETRYS;
2014             this.startInterval();
2015         },
2016
2017
2018         addListener: function(el, eventName, fn) {
2019             el = Roo.getDom(el);
2020             if (!el || !fn) {
2021                 return false;
2022             }
2023
2024             if ("unload" == eventName) {
2025                 unloadListeners[unloadListeners.length] =
2026                 [el, eventName, fn];
2027                 return true;
2028             }
2029
2030             var wrappedFn = function(e) {
2031                 return fn(Roo.lib.Event.getEvent(e));
2032             };
2033
2034             var li = [el, eventName, fn, wrappedFn];
2035
2036             var index = listeners.length;
2037             listeners[index] = li;
2038
2039             this.doAdd(el, eventName, wrappedFn, false);
2040             return true;
2041
2042         },
2043
2044
2045         removeListener: function(el, eventName, fn) {
2046             var i, len;
2047
2048             el = Roo.getDom(el);
2049
2050             if(!fn) {
2051                 return this.purgeElement(el, false, eventName);
2052             }
2053
2054
2055             if ("unload" == eventName) {
2056
2057                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2058                     var li = unloadListeners[i];
2059                     if (li &&
2060                         li[0] == el &&
2061                         li[1] == eventName &&
2062                         li[2] == fn) {
2063                         unloadListeners.splice(i, 1);
2064                         return true;
2065                     }
2066                 }
2067
2068                 return false;
2069             }
2070
2071             var cacheItem = null;
2072
2073
2074             var index = arguments[3];
2075
2076             if ("undefined" == typeof index) {
2077                 index = this._getCacheIndex(el, eventName, fn);
2078             }
2079
2080             if (index >= 0) {
2081                 cacheItem = listeners[index];
2082             }
2083
2084             if (!el || !cacheItem) {
2085                 return false;
2086             }
2087
2088             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2089
2090             delete listeners[index][this.WFN];
2091             delete listeners[index][this.FN];
2092             listeners.splice(index, 1);
2093
2094             return true;
2095
2096         },
2097
2098
2099         getTarget: function(ev, resolveTextNode) {
2100             ev = ev.browserEvent || ev;
2101             var t = ev.target || ev.srcElement;
2102             return this.resolveTextNode(t);
2103         },
2104
2105
2106         resolveTextNode: function(node) {
2107             if (Roo.isSafari && node && 3 == node.nodeType) {
2108                 return node.parentNode;
2109             } else {
2110                 return node;
2111             }
2112         },
2113
2114
2115         getPageX: function(ev) {
2116             ev = ev.browserEvent || ev;
2117             var x = ev.pageX;
2118             if (!x && 0 !== x) {
2119                 x = ev.clientX || 0;
2120
2121                 if (Roo.isIE) {
2122                     x += this.getScroll()[1];
2123                 }
2124             }
2125
2126             return x;
2127         },
2128
2129
2130         getPageY: function(ev) {
2131             ev = ev.browserEvent || ev;
2132             var y = ev.pageY;
2133             if (!y && 0 !== y) {
2134                 y = ev.clientY || 0;
2135
2136                 if (Roo.isIE) {
2137                     y += this.getScroll()[0];
2138                 }
2139             }
2140
2141
2142             return y;
2143         },
2144
2145
2146         getXY: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             return [this.getPageX(ev), this.getPageY(ev)];
2149         },
2150
2151
2152         getRelatedTarget: function(ev) {
2153             ev = ev.browserEvent || ev;
2154             var t = ev.relatedTarget;
2155             if (!t) {
2156                 if (ev.type == "mouseout") {
2157                     t = ev.toElement;
2158                 } else if (ev.type == "mouseover") {
2159                     t = ev.fromElement;
2160                 }
2161             }
2162
2163             return this.resolveTextNode(t);
2164         },
2165
2166
2167         getTime: function(ev) {
2168             ev = ev.browserEvent || ev;
2169             if (!ev.time) {
2170                 var t = new Date().getTime();
2171                 try {
2172                     ev.time = t;
2173                 } catch(ex) {
2174                     this.lastError = ex;
2175                     return t;
2176                 }
2177             }
2178
2179             return ev.time;
2180         },
2181
2182
2183         stopEvent: function(ev) {
2184             this.stopPropagation(ev);
2185             this.preventDefault(ev);
2186         },
2187
2188
2189         stopPropagation: function(ev) {
2190             ev = ev.browserEvent || ev;
2191             if (ev.stopPropagation) {
2192                 ev.stopPropagation();
2193             } else {
2194                 ev.cancelBubble = true;
2195             }
2196         },
2197
2198
2199         preventDefault: function(ev) {
2200             ev = ev.browserEvent || ev;
2201             if(ev.preventDefault) {
2202                 ev.preventDefault();
2203             } else {
2204                 ev.returnValue = false;
2205             }
2206         },
2207
2208
2209         getEvent: function(e) {
2210             var ev = e || window.event;
2211             if (!ev) {
2212                 var c = this.getEvent.caller;
2213                 while (c) {
2214                     ev = c.arguments[0];
2215                     if (ev && Event == ev.constructor) {
2216                         break;
2217                     }
2218                     c = c.caller;
2219                 }
2220             }
2221             return ev;
2222         },
2223
2224
2225         getCharCode: function(ev) {
2226             ev = ev.browserEvent || ev;
2227             return ev.charCode || ev.keyCode || 0;
2228         },
2229
2230
2231         _getCacheIndex: function(el, eventName, fn) {
2232             for (var i = 0,len = listeners.length; i < len; ++i) {
2233                 var li = listeners[i];
2234                 if (li &&
2235                     li[this.FN] == fn &&
2236                     li[this.EL] == el &&
2237                     li[this.TYPE] == eventName) {
2238                     return i;
2239                 }
2240             }
2241
2242             return -1;
2243         },
2244
2245
2246         elCache: {},
2247
2248
2249         getEl: function(id) {
2250             return document.getElementById(id);
2251         },
2252
2253
2254         clearCache: function() {
2255         },
2256
2257
2258         _load: function(e) {
2259             loadComplete = true;
2260             var EU = Roo.lib.Event;
2261
2262
2263             if (Roo.isIE) {
2264                 EU.doRemove(window, "load", EU._load);
2265             }
2266         },
2267
2268
2269         _tryPreloadAttach: function() {
2270
2271             if (this.locked) {
2272                 return false;
2273             }
2274
2275             this.locked = true;
2276
2277
2278             var tryAgain = !loadComplete;
2279             if (!tryAgain) {
2280                 tryAgain = (retryCount > 0);
2281             }
2282
2283
2284             var notAvail = [];
2285             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2286                 var item = onAvailStack[i];
2287                 if (item) {
2288                     var el = this.getEl(item.id);
2289
2290                     if (el) {
2291                         if (!item.checkReady ||
2292                             loadComplete ||
2293                             el.nextSibling ||
2294                             (document && document.body)) {
2295
2296                             var scope = el;
2297                             if (item.override) {
2298                                 if (item.override === true) {
2299                                     scope = item.obj;
2300                                 } else {
2301                                     scope = item.override;
2302                                 }
2303                             }
2304                             item.fn.call(scope, item.obj);
2305                             onAvailStack[i] = null;
2306                         }
2307                     } else {
2308                         notAvail.push(item);
2309                     }
2310                 }
2311             }
2312
2313             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2314
2315             if (tryAgain) {
2316
2317                 this.startInterval();
2318             } else {
2319                 clearInterval(this._interval);
2320                 this._interval = null;
2321             }
2322
2323             this.locked = false;
2324
2325             return true;
2326
2327         },
2328
2329
2330         purgeElement: function(el, recurse, eventName) {
2331             var elListeners = this.getListeners(el, eventName);
2332             if (elListeners) {
2333                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2334                     var l = elListeners[i];
2335                     this.removeListener(el, l.type, l.fn);
2336                 }
2337             }
2338
2339             if (recurse && el && el.childNodes) {
2340                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2341                     this.purgeElement(el.childNodes[i], recurse, eventName);
2342                 }
2343             }
2344         },
2345
2346
2347         getListeners: function(el, eventName) {
2348             var results = [], searchLists;
2349             if (!eventName) {
2350                 searchLists = [listeners, unloadListeners];
2351             } else if (eventName == "unload") {
2352                 searchLists = [unloadListeners];
2353             } else {
2354                 searchLists = [listeners];
2355             }
2356
2357             for (var j = 0; j < searchLists.length; ++j) {
2358                 var searchList = searchLists[j];
2359                 if (searchList && searchList.length > 0) {
2360                     for (var i = 0,len = searchList.length; i < len; ++i) {
2361                         var l = searchList[i];
2362                         if (l && l[this.EL] === el &&
2363                             (!eventName || eventName === l[this.TYPE])) {
2364                             results.push({
2365                                 type:   l[this.TYPE],
2366                                 fn:     l[this.FN],
2367                                 obj:    l[this.OBJ],
2368                                 adjust: l[this.ADJ_SCOPE],
2369                                 index:  i
2370                             });
2371                         }
2372                     }
2373                 }
2374             }
2375
2376             return (results.length) ? results : null;
2377         },
2378
2379
2380         _unload: function(e) {
2381
2382             var EU = Roo.lib.Event, i, j, l, len, index;
2383
2384             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2385                 l = unloadListeners[i];
2386                 if (l) {
2387                     var scope = window;
2388                     if (l[EU.ADJ_SCOPE]) {
2389                         if (l[EU.ADJ_SCOPE] === true) {
2390                             scope = l[EU.OBJ];
2391                         } else {
2392                             scope = l[EU.ADJ_SCOPE];
2393                         }
2394                     }
2395                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2396                     unloadListeners[i] = null;
2397                     l = null;
2398                     scope = null;
2399                 }
2400             }
2401
2402             unloadListeners = null;
2403
2404             if (listeners && listeners.length > 0) {
2405                 j = listeners.length;
2406                 while (j) {
2407                     index = j - 1;
2408                     l = listeners[index];
2409                     if (l) {
2410                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2411                                 l[EU.FN], index);
2412                     }
2413                     j = j - 1;
2414                 }
2415                 l = null;
2416
2417                 EU.clearCache();
2418             }
2419
2420             EU.doRemove(window, "unload", EU._unload);
2421
2422         },
2423
2424
2425         getScroll: function() {
2426             var dd = document.documentElement, db = document.body;
2427             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2428                 return [dd.scrollTop, dd.scrollLeft];
2429             } else if (db) {
2430                 return [db.scrollTop, db.scrollLeft];
2431             } else {
2432                 return [0, 0];
2433             }
2434         },
2435
2436
2437         doAdd: function () {
2438             if (window.addEventListener) {
2439                 return function(el, eventName, fn, capture) {
2440                     el.addEventListener(eventName, fn, (capture));
2441                 };
2442             } else if (window.attachEvent) {
2443                 return function(el, eventName, fn, capture) {
2444                     el.attachEvent("on" + eventName, fn);
2445                 };
2446             } else {
2447                 return function() {
2448                 };
2449             }
2450         }(),
2451
2452
2453         doRemove: function() {
2454             if (window.removeEventListener) {
2455                 return function (el, eventName, fn, capture) {
2456                     el.removeEventListener(eventName, fn, (capture));
2457                 };
2458             } else if (window.detachEvent) {
2459                 return function (el, eventName, fn) {
2460                     el.detachEvent("on" + eventName, fn);
2461                 };
2462             } else {
2463                 return function() {
2464                 };
2465             }
2466         }()
2467     };
2468     
2469 }();
2470 (function() {     
2471    
2472     var E = Roo.lib.Event;
2473     E.on = E.addListener;
2474     E.un = E.removeListener;
2475
2476     if (document && document.body) {
2477         E._load();
2478     } else {
2479         E.doAdd(window, "load", E._load);
2480     }
2481     E.doAdd(window, "unload", E._unload);
2482     E._tryPreloadAttach();
2483 })();
2484
2485 /*
2486  * Portions of this file are based on pieces of Yahoo User Interface Library
2487  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2488  * YUI licensed under the BSD License:
2489  * http://developer.yahoo.net/yui/license.txt
2490  * <script type="text/javascript">
2491  *
2492  */
2493
2494 (function() {
2495     /**
2496      * @class Roo.lib.Ajax
2497      *
2498      */
2499     Roo.lib.Ajax = {
2500         /**
2501          * @static 
2502          */
2503         request : function(method, uri, cb, data, options) {
2504             if(options){
2505                 var hs = options.headers;
2506                 if(hs){
2507                     for(var h in hs){
2508                         if(hs.hasOwnProperty(h)){
2509                             this.initHeader(h, hs[h], false);
2510                         }
2511                     }
2512                 }
2513                 if(options.xmlData){
2514                     this.initHeader('Content-Type', 'text/xml', false);
2515                     method = 'POST';
2516                     data = options.xmlData;
2517                 }
2518             }
2519
2520             return this.asyncRequest(method, uri, cb, data);
2521         },
2522
2523         serializeForm : function(form) {
2524             if(typeof form == 'string') {
2525                 form = (document.getElementById(form) || document.forms[form]);
2526             }
2527
2528             var el, name, val, disabled, data = '', hasSubmit = false;
2529             for (var i = 0; i < form.elements.length; i++) {
2530                 el = form.elements[i];
2531                 disabled = form.elements[i].disabled;
2532                 name = form.elements[i].name;
2533                 val = form.elements[i].value;
2534
2535                 if (!disabled && name){
2536                     switch (el.type)
2537                             {
2538                         case 'select-one':
2539                         case 'select-multiple':
2540                             for (var j = 0; j < el.options.length; j++) {
2541                                 if (el.options[j].selected) {
2542                                     if (Roo.isIE) {
2543                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2544                                     }
2545                                     else {
2546                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2547                                     }
2548                                 }
2549                             }
2550                             break;
2551                         case 'radio':
2552                         case 'checkbox':
2553                             if (el.checked) {
2554                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2555                             }
2556                             break;
2557                         case 'file':
2558
2559                         case undefined:
2560
2561                         case 'reset':
2562
2563                         case 'button':
2564
2565                             break;
2566                         case 'submit':
2567                             if(hasSubmit == false) {
2568                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2569                                 hasSubmit = true;
2570                             }
2571                             break;
2572                         default:
2573                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2574                             break;
2575                     }
2576                 }
2577             }
2578             data = data.substr(0, data.length - 1);
2579             return data;
2580         },
2581
2582         headers:{},
2583
2584         hasHeaders:false,
2585
2586         useDefaultHeader:true,
2587
2588         defaultPostHeader:'application/x-www-form-urlencoded',
2589
2590         useDefaultXhrHeader:true,
2591
2592         defaultXhrHeader:'XMLHttpRequest',
2593
2594         hasDefaultHeaders:true,
2595
2596         defaultHeaders:{},
2597
2598         poll:{},
2599
2600         timeout:{},
2601
2602         pollInterval:50,
2603
2604         transactionId:0,
2605
2606         setProgId:function(id)
2607         {
2608             this.activeX.unshift(id);
2609         },
2610
2611         setDefaultPostHeader:function(b)
2612         {
2613             this.useDefaultHeader = b;
2614         },
2615
2616         setDefaultXhrHeader:function(b)
2617         {
2618             this.useDefaultXhrHeader = b;
2619         },
2620
2621         setPollingInterval:function(i)
2622         {
2623             if (typeof i == 'number' && isFinite(i)) {
2624                 this.pollInterval = i;
2625             }
2626         },
2627
2628         createXhrObject:function(transactionId)
2629         {
2630             var obj,http;
2631             try
2632             {
2633
2634                 http = new XMLHttpRequest();
2635
2636                 obj = { conn:http, tId:transactionId };
2637             }
2638             catch(e)
2639             {
2640                 for (var i = 0; i < this.activeX.length; ++i) {
2641                     try
2642                     {
2643
2644                         http = new ActiveXObject(this.activeX[i]);
2645
2646                         obj = { conn:http, tId:transactionId };
2647                         break;
2648                     }
2649                     catch(e) {
2650                     }
2651                 }
2652             }
2653             finally
2654             {
2655                 return obj;
2656             }
2657         },
2658
2659         getConnectionObject:function()
2660         {
2661             var o;
2662             var tId = this.transactionId;
2663
2664             try
2665             {
2666                 o = this.createXhrObject(tId);
2667                 if (o) {
2668                     this.transactionId++;
2669                 }
2670             }
2671             catch(e) {
2672             }
2673             finally
2674             {
2675                 return o;
2676             }
2677         },
2678
2679         asyncRequest:function(method, uri, callback, postData)
2680         {
2681             var o = this.getConnectionObject();
2682
2683             if (!o) {
2684                 return null;
2685             }
2686             else {
2687                 o.conn.open(method, uri, true);
2688
2689                 if (this.useDefaultXhrHeader) {
2690                     if (!this.defaultHeaders['X-Requested-With']) {
2691                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2692                     }
2693                 }
2694
2695                 if(postData && this.useDefaultHeader){
2696                     this.initHeader('Content-Type', this.defaultPostHeader);
2697                 }
2698
2699                  if (this.hasDefaultHeaders || this.hasHeaders) {
2700                     this.setHeader(o);
2701                 }
2702
2703                 this.handleReadyState(o, callback);
2704                 o.conn.send(postData || null);
2705
2706                 return o;
2707             }
2708         },
2709
2710         handleReadyState:function(o, callback)
2711         {
2712             var oConn = this;
2713
2714             if (callback && callback.timeout) {
2715                 
2716                 this.timeout[o.tId] = window.setTimeout(function() {
2717                     oConn.abort(o, callback, true);
2718                 }, callback.timeout);
2719             }
2720
2721             this.poll[o.tId] = window.setInterval(
2722                     function() {
2723                         if (o.conn && o.conn.readyState == 4) {
2724                             window.clearInterval(oConn.poll[o.tId]);
2725                             delete oConn.poll[o.tId];
2726
2727                             if(callback && callback.timeout) {
2728                                 window.clearTimeout(oConn.timeout[o.tId]);
2729                                 delete oConn.timeout[o.tId];
2730                             }
2731
2732                             oConn.handleTransactionResponse(o, callback);
2733                         }
2734                     }
2735                     , this.pollInterval);
2736         },
2737
2738         handleTransactionResponse:function(o, callback, isAbort)
2739         {
2740
2741             if (!callback) {
2742                 this.releaseObject(o);
2743                 return;
2744             }
2745
2746             var httpStatus, responseObject;
2747
2748             try
2749             {
2750                 if (o.conn.status !== undefined && o.conn.status != 0) {
2751                     httpStatus = o.conn.status;
2752                 }
2753                 else {
2754                     httpStatus = 13030;
2755                 }
2756             }
2757             catch(e) {
2758
2759
2760                 httpStatus = 13030;
2761             }
2762
2763             if (httpStatus >= 200 && httpStatus < 300) {
2764                 responseObject = this.createResponseObject(o, callback.argument);
2765                 if (callback.success) {
2766                     if (!callback.scope) {
2767                         callback.success(responseObject);
2768                     }
2769                     else {
2770
2771
2772                         callback.success.apply(callback.scope, [responseObject]);
2773                     }
2774                 }
2775             }
2776             else {
2777                 switch (httpStatus) {
2778
2779                     case 12002:
2780                     case 12029:
2781                     case 12030:
2782                     case 12031:
2783                     case 12152:
2784                     case 13030:
2785                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2786                         if (callback.failure) {
2787                             if (!callback.scope) {
2788                                 callback.failure(responseObject);
2789                             }
2790                             else {
2791                                 callback.failure.apply(callback.scope, [responseObject]);
2792                             }
2793                         }
2794                         break;
2795                     default:
2796                         responseObject = this.createResponseObject(o, callback.argument);
2797                         if (callback.failure) {
2798                             if (!callback.scope) {
2799                                 callback.failure(responseObject);
2800                             }
2801                             else {
2802                                 callback.failure.apply(callback.scope, [responseObject]);
2803                             }
2804                         }
2805                 }
2806             }
2807
2808             this.releaseObject(o);
2809             responseObject = null;
2810         },
2811
2812         createResponseObject:function(o, callbackArg)
2813         {
2814             var obj = {};
2815             var headerObj = {};
2816
2817             try
2818             {
2819                 var headerStr = o.conn.getAllResponseHeaders();
2820                 var header = headerStr.split('\n');
2821                 for (var i = 0; i < header.length; i++) {
2822                     var delimitPos = header[i].indexOf(':');
2823                     if (delimitPos != -1) {
2824                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2825                     }
2826                 }
2827             }
2828             catch(e) {
2829             }
2830
2831             obj.tId = o.tId;
2832             obj.status = o.conn.status;
2833             obj.statusText = o.conn.statusText;
2834             obj.getResponseHeader = headerObj;
2835             obj.getAllResponseHeaders = headerStr;
2836             obj.responseText = o.conn.responseText;
2837             obj.responseXML = o.conn.responseXML;
2838
2839             if (typeof callbackArg !== undefined) {
2840                 obj.argument = callbackArg;
2841             }
2842
2843             return obj;
2844         },
2845
2846         createExceptionObject:function(tId, callbackArg, isAbort)
2847         {
2848             var COMM_CODE = 0;
2849             var COMM_ERROR = 'communication failure';
2850             var ABORT_CODE = -1;
2851             var ABORT_ERROR = 'transaction aborted';
2852
2853             var obj = {};
2854
2855             obj.tId = tId;
2856             if (isAbort) {
2857                 obj.status = ABORT_CODE;
2858                 obj.statusText = ABORT_ERROR;
2859             }
2860             else {
2861                 obj.status = COMM_CODE;
2862                 obj.statusText = COMM_ERROR;
2863             }
2864
2865             if (callbackArg) {
2866                 obj.argument = callbackArg;
2867             }
2868
2869             return obj;
2870         },
2871
2872         initHeader:function(label, value, isDefault)
2873         {
2874             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2875
2876             if (headerObj[label] === undefined) {
2877                 headerObj[label] = value;
2878             }
2879             else {
2880
2881
2882                 headerObj[label] = value + "," + headerObj[label];
2883             }
2884
2885             if (isDefault) {
2886                 this.hasDefaultHeaders = true;
2887             }
2888             else {
2889                 this.hasHeaders = true;
2890             }
2891         },
2892
2893
2894         setHeader:function(o)
2895         {
2896             if (this.hasDefaultHeaders) {
2897                 for (var prop in this.defaultHeaders) {
2898                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2899                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2900                     }
2901                 }
2902             }
2903
2904             if (this.hasHeaders) {
2905                 for (var prop in this.headers) {
2906                     if (this.headers.hasOwnProperty(prop)) {
2907                         o.conn.setRequestHeader(prop, this.headers[prop]);
2908                     }
2909                 }
2910                 this.headers = {};
2911                 this.hasHeaders = false;
2912             }
2913         },
2914
2915         resetDefaultHeaders:function() {
2916             delete this.defaultHeaders;
2917             this.defaultHeaders = {};
2918             this.hasDefaultHeaders = false;
2919         },
2920
2921         abort:function(o, callback, isTimeout)
2922         {
2923             if(this.isCallInProgress(o)) {
2924                 o.conn.abort();
2925                 window.clearInterval(this.poll[o.tId]);
2926                 delete this.poll[o.tId];
2927                 if (isTimeout) {
2928                     delete this.timeout[o.tId];
2929                 }
2930
2931                 this.handleTransactionResponse(o, callback, true);
2932
2933                 return true;
2934             }
2935             else {
2936                 return false;
2937             }
2938         },
2939
2940
2941         isCallInProgress:function(o)
2942         {
2943             if (o && o.conn) {
2944                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2945             }
2946             else {
2947
2948                 return false;
2949             }
2950         },
2951
2952
2953         releaseObject:function(o)
2954         {
2955
2956             o.conn = null;
2957
2958             o = null;
2959         },
2960
2961         activeX:[
2962         'MSXML2.XMLHTTP.3.0',
2963         'MSXML2.XMLHTTP',
2964         'Microsoft.XMLHTTP'
2965         ]
2966
2967
2968     };
2969 })();/*
2970  * Portions of this file are based on pieces of Yahoo User Interface Library
2971  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2972  * YUI licensed under the BSD License:
2973  * http://developer.yahoo.net/yui/license.txt
2974  * <script type="text/javascript">
2975  *
2976  */
2977
2978 Roo.lib.Region = function(t, r, b, l) {
2979     this.top = t;
2980     this[1] = t;
2981     this.right = r;
2982     this.bottom = b;
2983     this.left = l;
2984     this[0] = l;
2985 };
2986
2987
2988 Roo.lib.Region.prototype = {
2989     contains : function(region) {
2990         return ( region.left >= this.left &&
2991                  region.right <= this.right &&
2992                  region.top >= this.top &&
2993                  region.bottom <= this.bottom    );
2994
2995     },
2996
2997     getArea : function() {
2998         return ( (this.bottom - this.top) * (this.right - this.left) );
2999     },
3000
3001     intersect : function(region) {
3002         var t = Math.max(this.top, region.top);
3003         var r = Math.min(this.right, region.right);
3004         var b = Math.min(this.bottom, region.bottom);
3005         var l = Math.max(this.left, region.left);
3006
3007         if (b >= t && r >= l) {
3008             return new Roo.lib.Region(t, r, b, l);
3009         } else {
3010             return null;
3011         }
3012     },
3013     union : function(region) {
3014         var t = Math.min(this.top, region.top);
3015         var r = Math.max(this.right, region.right);
3016         var b = Math.max(this.bottom, region.bottom);
3017         var l = Math.min(this.left, region.left);
3018
3019         return new Roo.lib.Region(t, r, b, l);
3020     },
3021
3022     adjust : function(t, l, b, r) {
3023         this.top += t;
3024         this.left += l;
3025         this.right += r;
3026         this.bottom += b;
3027         return this;
3028     }
3029 };
3030
3031 Roo.lib.Region.getRegion = function(el) {
3032     var p = Roo.lib.Dom.getXY(el);
3033
3034     var t = p[1];
3035     var r = p[0] + el.offsetWidth;
3036     var b = p[1] + el.offsetHeight;
3037     var l = p[0];
3038
3039     return new Roo.lib.Region(t, r, b, l);
3040 };
3041 /*
3042  * Portions of this file are based on pieces of Yahoo User Interface Library
3043  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3044  * YUI licensed under the BSD License:
3045  * http://developer.yahoo.net/yui/license.txt
3046  * <script type="text/javascript">
3047  *
3048  */
3049 //@@dep Roo.lib.Region
3050
3051
3052 Roo.lib.Point = function(x, y) {
3053     if (x instanceof Array) {
3054         y = x[1];
3055         x = x[0];
3056     }
3057     this.x = this.right = this.left = this[0] = x;
3058     this.y = this.top = this.bottom = this[1] = y;
3059 };
3060
3061 Roo.lib.Point.prototype = new Roo.lib.Region();
3062 /*
3063  * Portions of this file are based on pieces of Yahoo User Interface Library
3064  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3065  * YUI licensed under the BSD License:
3066  * http://developer.yahoo.net/yui/license.txt
3067  * <script type="text/javascript">
3068  *
3069  */
3070  
3071 (function() {   
3072
3073     Roo.lib.Anim = {
3074         scroll : function(el, args, duration, easing, cb, scope) {
3075             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3076         },
3077
3078         motion : function(el, args, duration, easing, cb, scope) {
3079             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3080         },
3081
3082         color : function(el, args, duration, easing, cb, scope) {
3083             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3084         },
3085
3086         run : function(el, args, duration, easing, cb, scope, type) {
3087             type = type || Roo.lib.AnimBase;
3088             if (typeof easing == "string") {
3089                 easing = Roo.lib.Easing[easing];
3090             }
3091             var anim = new type(el, args, duration, easing);
3092             anim.animateX(function() {
3093                 Roo.callback(cb, scope);
3094             });
3095             return anim;
3096         }
3097     };
3098 })();/*
3099  * Portions of this file are based on pieces of Yahoo User Interface Library
3100  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3101  * YUI licensed under the BSD License:
3102  * http://developer.yahoo.net/yui/license.txt
3103  * <script type="text/javascript">
3104  *
3105  */
3106
3107 (function() {    
3108     var libFlyweight;
3109     
3110     function fly(el) {
3111         if (!libFlyweight) {
3112             libFlyweight = new Roo.Element.Flyweight();
3113         }
3114         libFlyweight.dom = el;
3115         return libFlyweight;
3116     }
3117
3118     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3119     
3120    
3121     
3122     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3123         if (el) {
3124             this.init(el, attributes, duration, method);
3125         }
3126     };
3127
3128     Roo.lib.AnimBase.fly = fly;
3129     
3130     
3131     
3132     Roo.lib.AnimBase.prototype = {
3133
3134         toString: function() {
3135             var el = this.getEl();
3136             var id = el.id || el.tagName;
3137             return ("Anim " + id);
3138         },
3139
3140         patterns: {
3141             noNegatives:        /width|height|opacity|padding/i,
3142             offsetAttribute:  /^((width|height)|(top|left))$/,
3143             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3144             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3145         },
3146
3147
3148         doMethod: function(attr, start, end) {
3149             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3150         },
3151
3152
3153         setAttribute: function(attr, val, unit) {
3154             if (this.patterns.noNegatives.test(attr)) {
3155                 val = (val > 0) ? val : 0;
3156             }
3157
3158             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3159         },
3160
3161
3162         getAttribute: function(attr) {
3163             var el = this.getEl();
3164             var val = fly(el).getStyle(attr);
3165
3166             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3167                 return parseFloat(val);
3168             }
3169
3170             var a = this.patterns.offsetAttribute.exec(attr) || [];
3171             var pos = !!( a[3] );
3172             var box = !!( a[2] );
3173
3174
3175             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3176                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3177             } else {
3178                 val = 0;
3179             }
3180
3181             return val;
3182         },
3183
3184
3185         getDefaultUnit: function(attr) {
3186             if (this.patterns.defaultUnit.test(attr)) {
3187                 return 'px';
3188             }
3189
3190             return '';
3191         },
3192
3193         animateX : function(callback, scope) {
3194             var f = function() {
3195                 this.onComplete.removeListener(f);
3196                 if (typeof callback == "function") {
3197                     callback.call(scope || this, this);
3198                 }
3199             };
3200             this.onComplete.addListener(f, this);
3201             this.animate();
3202         },
3203
3204
3205         setRuntimeAttribute: function(attr) {
3206             var start;
3207             var end;
3208             var attributes = this.attributes;
3209
3210             this.runtimeAttributes[attr] = {};
3211
3212             var isset = function(prop) {
3213                 return (typeof prop !== 'undefined');
3214             };
3215
3216             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3217                 return false;
3218             }
3219
3220             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3221
3222
3223             if (isset(attributes[attr]['to'])) {
3224                 end = attributes[attr]['to'];
3225             } else if (isset(attributes[attr]['by'])) {
3226                 if (start.constructor == Array) {
3227                     end = [];
3228                     for (var i = 0, len = start.length; i < len; ++i) {
3229                         end[i] = start[i] + attributes[attr]['by'][i];
3230                     }
3231                 } else {
3232                     end = start + attributes[attr]['by'];
3233                 }
3234             }
3235
3236             this.runtimeAttributes[attr].start = start;
3237             this.runtimeAttributes[attr].end = end;
3238
3239
3240             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3241         },
3242
3243
3244         init: function(el, attributes, duration, method) {
3245
3246             var isAnimated = false;
3247
3248
3249             var startTime = null;
3250
3251
3252             var actualFrames = 0;
3253
3254
3255             el = Roo.getDom(el);
3256
3257
3258             this.attributes = attributes || {};
3259
3260
3261             this.duration = duration || 1;
3262
3263
3264             this.method = method || Roo.lib.Easing.easeNone;
3265
3266
3267             this.useSeconds = true;
3268
3269
3270             this.currentFrame = 0;
3271
3272
3273             this.totalFrames = Roo.lib.AnimMgr.fps;
3274
3275
3276             this.getEl = function() {
3277                 return el;
3278             };
3279
3280
3281             this.isAnimated = function() {
3282                 return isAnimated;
3283             };
3284
3285
3286             this.getStartTime = function() {
3287                 return startTime;
3288             };
3289
3290             this.runtimeAttributes = {};
3291
3292
3293             this.animate = function() {
3294                 if (this.isAnimated()) {
3295                     return false;
3296                 }
3297
3298                 this.currentFrame = 0;
3299
3300                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3301
3302                 Roo.lib.AnimMgr.registerElement(this);
3303             };
3304
3305
3306             this.stop = function(finish) {
3307                 if (finish) {
3308                     this.currentFrame = this.totalFrames;
3309                     this._onTween.fire();
3310                 }
3311                 Roo.lib.AnimMgr.stop(this);
3312             };
3313
3314             var onStart = function() {
3315                 this.onStart.fire();
3316
3317                 this.runtimeAttributes = {};
3318                 for (var attr in this.attributes) {
3319                     this.setRuntimeAttribute(attr);
3320                 }
3321
3322                 isAnimated = true;
3323                 actualFrames = 0;
3324                 startTime = new Date();
3325             };
3326
3327
3328             var onTween = function() {
3329                 var data = {
3330                     duration: new Date() - this.getStartTime(),
3331                     currentFrame: this.currentFrame
3332                 };
3333
3334                 data.toString = function() {
3335                     return (
3336                             'duration: ' + data.duration +
3337                             ', currentFrame: ' + data.currentFrame
3338                             );
3339                 };
3340
3341                 this.onTween.fire(data);
3342
3343                 var runtimeAttributes = this.runtimeAttributes;
3344
3345                 for (var attr in runtimeAttributes) {
3346                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3347                 }
3348
3349                 actualFrames += 1;
3350             };
3351
3352             var onComplete = function() {
3353                 var actual_duration = (new Date() - startTime) / 1000 ;
3354
3355                 var data = {
3356                     duration: actual_duration,
3357                     frames: actualFrames,
3358                     fps: actualFrames / actual_duration
3359                 };
3360
3361                 data.toString = function() {
3362                     return (
3363                             'duration: ' + data.duration +
3364                             ', frames: ' + data.frames +
3365                             ', fps: ' + data.fps
3366                             );
3367                 };
3368
3369                 isAnimated = false;
3370                 actualFrames = 0;
3371                 this.onComplete.fire(data);
3372             };
3373
3374
3375             this._onStart = new Roo.util.Event(this);
3376             this.onStart = new Roo.util.Event(this);
3377             this.onTween = new Roo.util.Event(this);
3378             this._onTween = new Roo.util.Event(this);
3379             this.onComplete = new Roo.util.Event(this);
3380             this._onComplete = new Roo.util.Event(this);
3381             this._onStart.addListener(onStart);
3382             this._onTween.addListener(onTween);
3383             this._onComplete.addListener(onComplete);
3384         }
3385     };
3386 })();
3387 /*
3388  * Portions of this file are based on pieces of Yahoo User Interface Library
3389  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3390  * YUI licensed under the BSD License:
3391  * http://developer.yahoo.net/yui/license.txt
3392  * <script type="text/javascript">
3393  *
3394  */
3395
3396 Roo.lib.AnimMgr = new function() {
3397
3398         var thread = null;
3399
3400
3401         var queue = [];
3402
3403
3404         var tweenCount = 0;
3405
3406
3407         this.fps = 1000;
3408
3409
3410         this.delay = 1;
3411
3412
3413         this.registerElement = function(tween) {
3414             queue[queue.length] = tween;
3415             tweenCount += 1;
3416             tween._onStart.fire();
3417             this.start();
3418         };
3419
3420
3421         this.unRegister = function(tween, index) {
3422             tween._onComplete.fire();
3423             index = index || getIndex(tween);
3424             if (index != -1) {
3425                 queue.splice(index, 1);
3426             }
3427
3428             tweenCount -= 1;
3429             if (tweenCount <= 0) {
3430                 this.stop();
3431             }
3432         };
3433
3434
3435         this.start = function() {
3436             if (thread === null) {
3437                 thread = setInterval(this.run, this.delay);
3438             }
3439         };
3440
3441
3442         this.stop = function(tween) {
3443             if (!tween) {
3444                 clearInterval(thread);
3445
3446                 for (var i = 0, len = queue.length; i < len; ++i) {
3447                     if (queue[0].isAnimated()) {
3448                         this.unRegister(queue[0], 0);
3449                     }
3450                 }
3451
3452                 queue = [];
3453                 thread = null;
3454                 tweenCount = 0;
3455             }
3456             else {
3457                 this.unRegister(tween);
3458             }
3459         };
3460
3461
3462         this.run = function() {
3463             for (var i = 0, len = queue.length; i < len; ++i) {
3464                 var tween = queue[i];
3465                 if (!tween || !tween.isAnimated()) {
3466                     continue;
3467                 }
3468
3469                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3470                 {
3471                     tween.currentFrame += 1;
3472
3473                     if (tween.useSeconds) {
3474                         correctFrame(tween);
3475                     }
3476                     tween._onTween.fire();
3477                 }
3478                 else {
3479                     Roo.lib.AnimMgr.stop(tween, i);
3480                 }
3481             }
3482         };
3483
3484         var getIndex = function(anim) {
3485             for (var i = 0, len = queue.length; i < len; ++i) {
3486                 if (queue[i] == anim) {
3487                     return i;
3488                 }
3489             }
3490             return -1;
3491         };
3492
3493
3494         var correctFrame = function(tween) {
3495             var frames = tween.totalFrames;
3496             var frame = tween.currentFrame;
3497             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3498             var elapsed = (new Date() - tween.getStartTime());
3499             var tweak = 0;
3500
3501             if (elapsed < tween.duration * 1000) {
3502                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3503             } else {
3504                 tweak = frames - (frame + 1);
3505             }
3506             if (tweak > 0 && isFinite(tweak)) {
3507                 if (tween.currentFrame + tweak >= frames) {
3508                     tweak = frames - (frame + 1);
3509                 }
3510
3511                 tween.currentFrame += tweak;
3512             }
3513         };
3514     };/*
3515  * Portions of this file are based on pieces of Yahoo User Interface Library
3516  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3517  * YUI licensed under the BSD License:
3518  * http://developer.yahoo.net/yui/license.txt
3519  * <script type="text/javascript">
3520  *
3521  */
3522 Roo.lib.Bezier = new function() {
3523
3524         this.getPosition = function(points, t) {
3525             var n = points.length;
3526             var tmp = [];
3527
3528             for (var i = 0; i < n; ++i) {
3529                 tmp[i] = [points[i][0], points[i][1]];
3530             }
3531
3532             for (var j = 1; j < n; ++j) {
3533                 for (i = 0; i < n - j; ++i) {
3534                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3535                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3536                 }
3537             }
3538
3539             return [ tmp[0][0], tmp[0][1] ];
3540
3541         };
3542     };/*
3543  * Portions of this file are based on pieces of Yahoo User Interface Library
3544  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3545  * YUI licensed under the BSD License:
3546  * http://developer.yahoo.net/yui/license.txt
3547  * <script type="text/javascript">
3548  *
3549  */
3550 (function() {
3551
3552     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3553         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3554     };
3555
3556     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3557
3558     var fly = Roo.lib.AnimBase.fly;
3559     var Y = Roo.lib;
3560     var superclass = Y.ColorAnim.superclass;
3561     var proto = Y.ColorAnim.prototype;
3562
3563     proto.toString = function() {
3564         var el = this.getEl();
3565         var id = el.id || el.tagName;
3566         return ("ColorAnim " + id);
3567     };
3568
3569     proto.patterns.color = /color$/i;
3570     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3571     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3572     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3573     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3574
3575
3576     proto.parseColor = function(s) {
3577         if (s.length == 3) {
3578             return s;
3579         }
3580
3581         var c = this.patterns.hex.exec(s);
3582         if (c && c.length == 4) {
3583             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3584         }
3585
3586         c = this.patterns.rgb.exec(s);
3587         if (c && c.length == 4) {
3588             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3589         }
3590
3591         c = this.patterns.hex3.exec(s);
3592         if (c && c.length == 4) {
3593             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3594         }
3595
3596         return null;
3597     };
3598     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3599     proto.getAttribute = function(attr) {
3600         var el = this.getEl();
3601         if (this.patterns.color.test(attr)) {
3602             var val = fly(el).getStyle(attr);
3603
3604             if (this.patterns.transparent.test(val)) {
3605                 var parent = el.parentNode;
3606                 val = fly(parent).getStyle(attr);
3607
3608                 while (parent && this.patterns.transparent.test(val)) {
3609                     parent = parent.parentNode;
3610                     val = fly(parent).getStyle(attr);
3611                     if (parent.tagName.toUpperCase() == 'HTML') {
3612                         val = '#fff';
3613                     }
3614                 }
3615             }
3616         } else {
3617             val = superclass.getAttribute.call(this, attr);
3618         }
3619
3620         return val;
3621     };
3622     proto.getAttribute = function(attr) {
3623         var el = this.getEl();
3624         if (this.patterns.color.test(attr)) {
3625             var val = fly(el).getStyle(attr);
3626
3627             if (this.patterns.transparent.test(val)) {
3628                 var parent = el.parentNode;
3629                 val = fly(parent).getStyle(attr);
3630
3631                 while (parent && this.patterns.transparent.test(val)) {
3632                     parent = parent.parentNode;
3633                     val = fly(parent).getStyle(attr);
3634                     if (parent.tagName.toUpperCase() == 'HTML') {
3635                         val = '#fff';
3636                     }
3637                 }
3638             }
3639         } else {
3640             val = superclass.getAttribute.call(this, attr);
3641         }
3642
3643         return val;
3644     };
3645
3646     proto.doMethod = function(attr, start, end) {
3647         var val;
3648
3649         if (this.patterns.color.test(attr)) {
3650             val = [];
3651             for (var i = 0, len = start.length; i < len; ++i) {
3652                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3653             }
3654
3655             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3656         }
3657         else {
3658             val = superclass.doMethod.call(this, attr, start, end);
3659         }
3660
3661         return val;
3662     };
3663
3664     proto.setRuntimeAttribute = function(attr) {
3665         superclass.setRuntimeAttribute.call(this, attr);
3666
3667         if (this.patterns.color.test(attr)) {
3668             var attributes = this.attributes;
3669             var start = this.parseColor(this.runtimeAttributes[attr].start);
3670             var end = this.parseColor(this.runtimeAttributes[attr].end);
3671
3672             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3673                 end = this.parseColor(attributes[attr].by);
3674
3675                 for (var i = 0, len = start.length; i < len; ++i) {
3676                     end[i] = start[i] + end[i];
3677                 }
3678             }
3679
3680             this.runtimeAttributes[attr].start = start;
3681             this.runtimeAttributes[attr].end = end;
3682         }
3683     };
3684 })();
3685
3686 /*
3687  * Portions of this file are based on pieces of Yahoo User Interface Library
3688  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3689  * YUI licensed under the BSD License:
3690  * http://developer.yahoo.net/yui/license.txt
3691  * <script type="text/javascript">
3692  *
3693  */
3694 Roo.lib.Easing = {
3695
3696
3697     easeNone: function (t, b, c, d) {
3698         return c * t / d + b;
3699     },
3700
3701
3702     easeIn: function (t, b, c, d) {
3703         return c * (t /= d) * t + b;
3704     },
3705
3706
3707     easeOut: function (t, b, c, d) {
3708         return -c * (t /= d) * (t - 2) + b;
3709     },
3710
3711
3712     easeBoth: function (t, b, c, d) {
3713         if ((t /= d / 2) < 1) {
3714             return c / 2 * t * t + b;
3715         }
3716
3717         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3718     },
3719
3720
3721     easeInStrong: function (t, b, c, d) {
3722         return c * (t /= d) * t * t * t + b;
3723     },
3724
3725
3726     easeOutStrong: function (t, b, c, d) {
3727         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3728     },
3729
3730
3731     easeBothStrong: function (t, b, c, d) {
3732         if ((t /= d / 2) < 1) {
3733             return c / 2 * t * t * t * t + b;
3734         }
3735
3736         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3737     },
3738
3739
3740
3741     elasticIn: function (t, b, c, d, a, p) {
3742         if (t == 0) {
3743             return b;
3744         }
3745         if ((t /= d) == 1) {
3746             return b + c;
3747         }
3748         if (!p) {
3749             p = d * .3;
3750         }
3751
3752         if (!a || a < Math.abs(c)) {
3753             a = c;
3754             var s = p / 4;
3755         }
3756         else {
3757             var s = p / (2 * Math.PI) * Math.asin(c / a);
3758         }
3759
3760         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3761     },
3762
3763
3764     elasticOut: function (t, b, c, d, a, p) {
3765         if (t == 0) {
3766             return b;
3767         }
3768         if ((t /= d) == 1) {
3769             return b + c;
3770         }
3771         if (!p) {
3772             p = d * .3;
3773         }
3774
3775         if (!a || a < Math.abs(c)) {
3776             a = c;
3777             var s = p / 4;
3778         }
3779         else {
3780             var s = p / (2 * Math.PI) * Math.asin(c / a);
3781         }
3782
3783         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3784     },
3785
3786
3787     elasticBoth: function (t, b, c, d, a, p) {
3788         if (t == 0) {
3789             return b;
3790         }
3791
3792         if ((t /= d / 2) == 2) {
3793             return b + c;
3794         }
3795
3796         if (!p) {
3797             p = d * (.3 * 1.5);
3798         }
3799
3800         if (!a || a < Math.abs(c)) {
3801             a = c;
3802             var s = p / 4;
3803         }
3804         else {
3805             var s = p / (2 * Math.PI) * Math.asin(c / a);
3806         }
3807
3808         if (t < 1) {
3809             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3810                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3811         }
3812         return a * Math.pow(2, -10 * (t -= 1)) *
3813                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3814     },
3815
3816
3817
3818     backIn: function (t, b, c, d, s) {
3819         if (typeof s == 'undefined') {
3820             s = 1.70158;
3821         }
3822         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3823     },
3824
3825
3826     backOut: function (t, b, c, d, s) {
3827         if (typeof s == 'undefined') {
3828             s = 1.70158;
3829         }
3830         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3831     },
3832
3833
3834     backBoth: function (t, b, c, d, s) {
3835         if (typeof s == 'undefined') {
3836             s = 1.70158;
3837         }
3838
3839         if ((t /= d / 2 ) < 1) {
3840             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3841         }
3842         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3843     },
3844
3845
3846     bounceIn: function (t, b, c, d) {
3847         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3848     },
3849
3850
3851     bounceOut: function (t, b, c, d) {
3852         if ((t /= d) < (1 / 2.75)) {
3853             return c * (7.5625 * t * t) + b;
3854         } else if (t < (2 / 2.75)) {
3855             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3856         } else if (t < (2.5 / 2.75)) {
3857             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3858         }
3859         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3860     },
3861
3862
3863     bounceBoth: function (t, b, c, d) {
3864         if (t < d / 2) {
3865             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3866         }
3867         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3868     }
3869 };/*
3870  * Portions of this file are based on pieces of Yahoo User Interface Library
3871  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3872  * YUI licensed under the BSD License:
3873  * http://developer.yahoo.net/yui/license.txt
3874  * <script type="text/javascript">
3875  *
3876  */
3877     (function() {
3878         Roo.lib.Motion = function(el, attributes, duration, method) {
3879             if (el) {
3880                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3881             }
3882         };
3883
3884         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3885
3886
3887         var Y = Roo.lib;
3888         var superclass = Y.Motion.superclass;
3889         var proto = Y.Motion.prototype;
3890
3891         proto.toString = function() {
3892             var el = this.getEl();
3893             var id = el.id || el.tagName;
3894             return ("Motion " + id);
3895         };
3896
3897         proto.patterns.points = /^points$/i;
3898
3899         proto.setAttribute = function(attr, val, unit) {
3900             if (this.patterns.points.test(attr)) {
3901                 unit = unit || 'px';
3902                 superclass.setAttribute.call(this, 'left', val[0], unit);
3903                 superclass.setAttribute.call(this, 'top', val[1], unit);
3904             } else {
3905                 superclass.setAttribute.call(this, attr, val, unit);
3906             }
3907         };
3908
3909         proto.getAttribute = function(attr) {
3910             if (this.patterns.points.test(attr)) {
3911                 var val = [
3912                         superclass.getAttribute.call(this, 'left'),
3913                         superclass.getAttribute.call(this, 'top')
3914                         ];
3915             } else {
3916                 val = superclass.getAttribute.call(this, attr);
3917             }
3918
3919             return val;
3920         };
3921
3922         proto.doMethod = function(attr, start, end) {
3923             var val = null;
3924
3925             if (this.patterns.points.test(attr)) {
3926                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3927                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3928             } else {
3929                 val = superclass.doMethod.call(this, attr, start, end);
3930             }
3931             return val;
3932         };
3933
3934         proto.setRuntimeAttribute = function(attr) {
3935             if (this.patterns.points.test(attr)) {
3936                 var el = this.getEl();
3937                 var attributes = this.attributes;
3938                 var start;
3939                 var control = attributes['points']['control'] || [];
3940                 var end;
3941                 var i, len;
3942
3943                 if (control.length > 0 && !(control[0] instanceof Array)) {
3944                     control = [control];
3945                 } else {
3946                     var tmp = [];
3947                     for (i = 0,len = control.length; i < len; ++i) {
3948                         tmp[i] = control[i];
3949                     }
3950                     control = tmp;
3951                 }
3952
3953                 Roo.fly(el).position();
3954
3955                 if (isset(attributes['points']['from'])) {
3956                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3957                 }
3958                 else {
3959                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3960                 }
3961
3962                 start = this.getAttribute('points');
3963
3964
3965                 if (isset(attributes['points']['to'])) {
3966                     end = translateValues.call(this, attributes['points']['to'], start);
3967
3968                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3969                     for (i = 0,len = control.length; i < len; ++i) {
3970                         control[i] = translateValues.call(this, control[i], start);
3971                     }
3972
3973
3974                 } else if (isset(attributes['points']['by'])) {
3975                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3976
3977                     for (i = 0,len = control.length; i < len; ++i) {
3978                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3979                     }
3980                 }
3981
3982                 this.runtimeAttributes[attr] = [start];
3983
3984                 if (control.length > 0) {
3985                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3986                 }
3987
3988                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3989             }
3990             else {
3991                 superclass.setRuntimeAttribute.call(this, attr);
3992             }
3993         };
3994
3995         var translateValues = function(val, start) {
3996             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3997             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3998
3999             return val;
4000         };
4001
4002         var isset = function(prop) {
4003             return (typeof prop !== 'undefined');
4004         };
4005     })();
4006 /*
4007  * Portions of this file are based on pieces of Yahoo User Interface Library
4008  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4009  * YUI licensed under the BSD License:
4010  * http://developer.yahoo.net/yui/license.txt
4011  * <script type="text/javascript">
4012  *
4013  */
4014     (function() {
4015         Roo.lib.Scroll = function(el, attributes, duration, method) {
4016             if (el) {
4017                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4018             }
4019         };
4020
4021         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4022
4023
4024         var Y = Roo.lib;
4025         var superclass = Y.Scroll.superclass;
4026         var proto = Y.Scroll.prototype;
4027
4028         proto.toString = function() {
4029             var el = this.getEl();
4030             var id = el.id || el.tagName;
4031             return ("Scroll " + id);
4032         };
4033
4034         proto.doMethod = function(attr, start, end) {
4035             var val = null;
4036
4037             if (attr == 'scroll') {
4038                 val = [
4039                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4040                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4041                         ];
4042
4043             } else {
4044                 val = superclass.doMethod.call(this, attr, start, end);
4045             }
4046             return val;
4047         };
4048
4049         proto.getAttribute = function(attr) {
4050             var val = null;
4051             var el = this.getEl();
4052
4053             if (attr == 'scroll') {
4054                 val = [ el.scrollLeft, el.scrollTop ];
4055             } else {
4056                 val = superclass.getAttribute.call(this, attr);
4057             }
4058
4059             return val;
4060         };
4061
4062         proto.setAttribute = function(attr, val, unit) {
4063             var el = this.getEl();
4064
4065             if (attr == 'scroll') {
4066                 el.scrollLeft = val[0];
4067                 el.scrollTop = val[1];
4068             } else {
4069                 superclass.setAttribute.call(this, attr, val, unit);
4070             }
4071         };
4072     })();
4073 /*
4074  * Based on:
4075  * Ext JS Library 1.1.1
4076  * Copyright(c) 2006-2007, Ext JS, LLC.
4077  *
4078  * Originally Released Under LGPL - original licence link has changed is not relivant.
4079  *
4080  * Fork - LGPL
4081  * <script type="text/javascript">
4082  */
4083
4084
4085 // nasty IE9 hack - what a pile of crap that is..
4086
4087  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4088     Range.prototype.createContextualFragment = function (html) {
4089         var doc = window.document;
4090         var container = doc.createElement("div");
4091         container.innerHTML = html;
4092         var frag = doc.createDocumentFragment(), n;
4093         while ((n = container.firstChild)) {
4094             frag.appendChild(n);
4095         }
4096         return frag;
4097     };
4098 }
4099
4100 /**
4101  * @class Roo.DomHelper
4102  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4103  * 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>.
4104  * @singleton
4105  */
4106 Roo.DomHelper = function(){
4107     var tempTableEl = null;
4108     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4109     var tableRe = /^table|tbody|tr|td$/i;
4110     var xmlns = {};
4111     // build as innerHTML where available
4112     /** @ignore */
4113     var createHtml = function(o){
4114         if(typeof o == 'string'){
4115             return o;
4116         }
4117         var b = "";
4118         if(!o.tag){
4119             o.tag = "div";
4120         }
4121         b += "<" + o.tag;
4122         for(var attr in o){
4123             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4124             if(attr == "style"){
4125                 var s = o["style"];
4126                 if(typeof s == "function"){
4127                     s = s.call();
4128                 }
4129                 if(typeof s == "string"){
4130                     b += ' style="' + s + '"';
4131                 }else if(typeof s == "object"){
4132                     b += ' style="';
4133                     for(var key in s){
4134                         if(typeof s[key] != "function"){
4135                             b += key + ":" + s[key] + ";";
4136                         }
4137                     }
4138                     b += '"';
4139                 }
4140             }else{
4141                 if(attr == "cls"){
4142                     b += ' class="' + o["cls"] + '"';
4143                 }else if(attr == "htmlFor"){
4144                     b += ' for="' + o["htmlFor"] + '"';
4145                 }else{
4146                     b += " " + attr + '="' + o[attr] + '"';
4147                 }
4148             }
4149         }
4150         if(emptyTags.test(o.tag)){
4151             b += "/>";
4152         }else{
4153             b += ">";
4154             var cn = o.children || o.cn;
4155             if(cn){
4156                 //http://bugs.kde.org/show_bug.cgi?id=71506
4157                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4158                     for(var i = 0, len = cn.length; i < len; i++) {
4159                         b += createHtml(cn[i], b);
4160                     }
4161                 }else{
4162                     b += createHtml(cn, b);
4163                 }
4164             }
4165             if(o.html){
4166                 b += o.html;
4167             }
4168             b += "</" + o.tag + ">";
4169         }
4170         return b;
4171     };
4172
4173     // build as dom
4174     /** @ignore */
4175     var createDom = function(o, parentNode){
4176          
4177         // defininition craeted..
4178         var ns = false;
4179         if (o.ns && o.ns != 'html') {
4180                
4181             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4182                 xmlns[o.ns] = o.xmlns;
4183                 ns = o.xmlns;
4184             }
4185             if (typeof(xmlns[o.ns]) == 'undefined') {
4186                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4187             }
4188             ns = xmlns[o.ns];
4189         }
4190         
4191         
4192         if (typeof(o) == 'string') {
4193             return parentNode.appendChild(document.createTextNode(o));
4194         }
4195         o.tag = o.tag || div;
4196         if (o.ns && Roo.isIE) {
4197             ns = false;
4198             o.tag = o.ns + ':' + o.tag;
4199             
4200         }
4201         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4202         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4203         for(var attr in o){
4204             
4205             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4206                     attr == "style" || typeof o[attr] == "function") continue;
4207                     
4208             if(attr=="cls" && Roo.isIE){
4209                 el.className = o["cls"];
4210             }else{
4211                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4212                 else el[attr] = o[attr];
4213             }
4214         }
4215         Roo.DomHelper.applyStyles(el, o.style);
4216         var cn = o.children || o.cn;
4217         if(cn){
4218             //http://bugs.kde.org/show_bug.cgi?id=71506
4219              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4220                 for(var i = 0, len = cn.length; i < len; i++) {
4221                     createDom(cn[i], el);
4222                 }
4223             }else{
4224                 createDom(cn, el);
4225             }
4226         }
4227         if(o.html){
4228             el.innerHTML = o.html;
4229         }
4230         if(parentNode){
4231            parentNode.appendChild(el);
4232         }
4233         return el;
4234     };
4235
4236     var ieTable = function(depth, s, h, e){
4237         tempTableEl.innerHTML = [s, h, e].join('');
4238         var i = -1, el = tempTableEl;
4239         while(++i < depth){
4240             el = el.firstChild;
4241         }
4242         return el;
4243     };
4244
4245     // kill repeat to save bytes
4246     var ts = '<table>',
4247         te = '</table>',
4248         tbs = ts+'<tbody>',
4249         tbe = '</tbody>'+te,
4250         trs = tbs + '<tr>',
4251         tre = '</tr>'+tbe;
4252
4253     /**
4254      * @ignore
4255      * Nasty code for IE's broken table implementation
4256      */
4257     var insertIntoTable = function(tag, where, el, html){
4258         if(!tempTableEl){
4259             tempTableEl = document.createElement('div');
4260         }
4261         var node;
4262         var before = null;
4263         if(tag == 'td'){
4264             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4265                 return;
4266             }
4267             if(where == 'beforebegin'){
4268                 before = el;
4269                 el = el.parentNode;
4270             } else{
4271                 before = el.nextSibling;
4272                 el = el.parentNode;
4273             }
4274             node = ieTable(4, trs, html, tre);
4275         }
4276         else if(tag == 'tr'){
4277             if(where == 'beforebegin'){
4278                 before = el;
4279                 el = el.parentNode;
4280                 node = ieTable(3, tbs, html, tbe);
4281             } else if(where == 'afterend'){
4282                 before = el.nextSibling;
4283                 el = el.parentNode;
4284                 node = ieTable(3, tbs, html, tbe);
4285             } else{ // INTO a TR
4286                 if(where == 'afterbegin'){
4287                     before = el.firstChild;
4288                 }
4289                 node = ieTable(4, trs, html, tre);
4290             }
4291         } else if(tag == 'tbody'){
4292             if(where == 'beforebegin'){
4293                 before = el;
4294                 el = el.parentNode;
4295                 node = ieTable(2, ts, html, te);
4296             } else if(where == 'afterend'){
4297                 before = el.nextSibling;
4298                 el = el.parentNode;
4299                 node = ieTable(2, ts, html, te);
4300             } else{
4301                 if(where == 'afterbegin'){
4302                     before = el.firstChild;
4303                 }
4304                 node = ieTable(3, tbs, html, tbe);
4305             }
4306         } else{ // TABLE
4307             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4308                 return;
4309             }
4310             if(where == 'afterbegin'){
4311                 before = el.firstChild;
4312             }
4313             node = ieTable(2, ts, html, te);
4314         }
4315         el.insertBefore(node, before);
4316         return node;
4317     };
4318
4319     return {
4320     /** True to force the use of DOM instead of html fragments @type Boolean */
4321     useDom : false,
4322
4323     /**
4324      * Returns the markup for the passed Element(s) config
4325      * @param {Object} o The Dom object spec (and children)
4326      * @return {String}
4327      */
4328     markup : function(o){
4329         return createHtml(o);
4330     },
4331
4332     /**
4333      * Applies a style specification to an element
4334      * @param {String/HTMLElement} el The element to apply styles to
4335      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4336      * a function which returns such a specification.
4337      */
4338     applyStyles : function(el, styles){
4339         if(styles){
4340            el = Roo.fly(el);
4341            if(typeof styles == "string"){
4342                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4343                var matches;
4344                while ((matches = re.exec(styles)) != null){
4345                    el.setStyle(matches[1], matches[2]);
4346                }
4347            }else if (typeof styles == "object"){
4348                for (var style in styles){
4349                   el.setStyle(style, styles[style]);
4350                }
4351            }else if (typeof styles == "function"){
4352                 Roo.DomHelper.applyStyles(el, styles.call());
4353            }
4354         }
4355     },
4356
4357     /**
4358      * Inserts an HTML fragment into the Dom
4359      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4360      * @param {HTMLElement} el The context element
4361      * @param {String} html The HTML fragmenet
4362      * @return {HTMLElement} The new node
4363      */
4364     insertHtml : function(where, el, html){
4365         where = where.toLowerCase();
4366         if(el.insertAdjacentHTML){
4367             if(tableRe.test(el.tagName)){
4368                 var rs;
4369                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4370                     return rs;
4371                 }
4372             }
4373             switch(where){
4374                 case "beforebegin":
4375                     el.insertAdjacentHTML('BeforeBegin', html);
4376                     return el.previousSibling;
4377                 case "afterbegin":
4378                     el.insertAdjacentHTML('AfterBegin', html);
4379                     return el.firstChild;
4380                 case "beforeend":
4381                     el.insertAdjacentHTML('BeforeEnd', html);
4382                     return el.lastChild;
4383                 case "afterend":
4384                     el.insertAdjacentHTML('AfterEnd', html);
4385                     return el.nextSibling;
4386             }
4387             throw 'Illegal insertion point -> "' + where + '"';
4388         }
4389         var range = el.ownerDocument.createRange();
4390         var frag;
4391         switch(where){
4392              case "beforebegin":
4393                 range.setStartBefore(el);
4394                 frag = range.createContextualFragment(html);
4395                 el.parentNode.insertBefore(frag, el);
4396                 return el.previousSibling;
4397              case "afterbegin":
4398                 if(el.firstChild){
4399                     range.setStartBefore(el.firstChild);
4400                     frag = range.createContextualFragment(html);
4401                     el.insertBefore(frag, el.firstChild);
4402                     return el.firstChild;
4403                 }else{
4404                     el.innerHTML = html;
4405                     return el.firstChild;
4406                 }
4407             case "beforeend":
4408                 if(el.lastChild){
4409                     range.setStartAfter(el.lastChild);
4410                     frag = range.createContextualFragment(html);
4411                     el.appendChild(frag);
4412                     return el.lastChild;
4413                 }else{
4414                     el.innerHTML = html;
4415                     return el.lastChild;
4416                 }
4417             case "afterend":
4418                 range.setStartAfter(el);
4419                 frag = range.createContextualFragment(html);
4420                 el.parentNode.insertBefore(frag, el.nextSibling);
4421                 return el.nextSibling;
4422             }
4423             throw 'Illegal insertion point -> "' + where + '"';
4424     },
4425
4426     /**
4427      * Creates new Dom element(s) and inserts them before el
4428      * @param {String/HTMLElement/Element} el The context element
4429      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4430      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4431      * @return {HTMLElement/Roo.Element} The new node
4432      */
4433     insertBefore : function(el, o, returnElement){
4434         return this.doInsert(el, o, returnElement, "beforeBegin");
4435     },
4436
4437     /**
4438      * Creates new Dom element(s) and inserts them after el
4439      * @param {String/HTMLElement/Element} el The context element
4440      * @param {Object} o The Dom object spec (and children)
4441      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4442      * @return {HTMLElement/Roo.Element} The new node
4443      */
4444     insertAfter : function(el, o, returnElement){
4445         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4446     },
4447
4448     /**
4449      * Creates new Dom element(s) and inserts them as the first child of el
4450      * @param {String/HTMLElement/Element} el The context element
4451      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4452      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4453      * @return {HTMLElement/Roo.Element} The new node
4454      */
4455     insertFirst : function(el, o, returnElement){
4456         return this.doInsert(el, o, returnElement, "afterBegin");
4457     },
4458
4459     // private
4460     doInsert : function(el, o, returnElement, pos, sibling){
4461         el = Roo.getDom(el);
4462         var newNode;
4463         if(this.useDom || o.ns){
4464             newNode = createDom(o, null);
4465             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4466         }else{
4467             var html = createHtml(o);
4468             newNode = this.insertHtml(pos, el, html);
4469         }
4470         return returnElement ? Roo.get(newNode, true) : newNode;
4471     },
4472
4473     /**
4474      * Creates new Dom element(s) and appends them to el
4475      * @param {String/HTMLElement/Element} el The context element
4476      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4477      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4478      * @return {HTMLElement/Roo.Element} The new node
4479      */
4480     append : function(el, o, returnElement){
4481         el = Roo.getDom(el);
4482         var newNode;
4483         if(this.useDom || o.ns){
4484             newNode = createDom(o, null);
4485             el.appendChild(newNode);
4486         }else{
4487             var html = createHtml(o);
4488             newNode = this.insertHtml("beforeEnd", el, html);
4489         }
4490         return returnElement ? Roo.get(newNode, true) : newNode;
4491     },
4492
4493     /**
4494      * Creates new Dom element(s) and overwrites the contents of el with them
4495      * @param {String/HTMLElement/Element} el The context element
4496      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4497      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4498      * @return {HTMLElement/Roo.Element} The new node
4499      */
4500     overwrite : function(el, o, returnElement){
4501         el = Roo.getDom(el);
4502         if (o.ns) {
4503           
4504             while (el.childNodes.length) {
4505                 el.removeChild(el.firstChild);
4506             }
4507             createDom(o, el);
4508         } else {
4509             el.innerHTML = createHtml(o);   
4510         }
4511         
4512         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4513     },
4514
4515     /**
4516      * Creates a new Roo.DomHelper.Template from the Dom object spec
4517      * @param {Object} o The Dom object spec (and children)
4518      * @return {Roo.DomHelper.Template} The new template
4519      */
4520     createTemplate : function(o){
4521         var html = createHtml(o);
4522         return new Roo.Template(html);
4523     }
4524     };
4525 }();
4526 /*
4527  * Based on:
4528  * Ext JS Library 1.1.1
4529  * Copyright(c) 2006-2007, Ext JS, LLC.
4530  *
4531  * Originally Released Under LGPL - original licence link has changed is not relivant.
4532  *
4533  * Fork - LGPL
4534  * <script type="text/javascript">
4535  */
4536  
4537 /**
4538 * @class Roo.Template
4539 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4540 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4541 * Usage:
4542 <pre><code>
4543 var t = new Roo.Template({
4544     html :  '&lt;div name="{id}"&gt;' + 
4545         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4546         '&lt;/div&gt;',
4547     myformat: function (value, allValues) {
4548         return 'XX' + value;
4549     }
4550 });
4551 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4552 </code></pre>
4553 * For more information see this blog post with examples:
4554 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4555      - Create Elements using DOM, HTML fragments and Templates</a>. 
4556 * @constructor
4557 * @param {Object} cfg - Configuration object.
4558 */
4559 Roo.Template = function(cfg){
4560     // BC!
4561     if(cfg instanceof Array){
4562         cfg = cfg.join("");
4563     }else if(arguments.length > 1){
4564         cfg = Array.prototype.join.call(arguments, "");
4565     }
4566     
4567     
4568     if (typeof(cfg) == 'object') {
4569         Roo.apply(this,cfg)
4570     } else {
4571         // bc
4572         this.html = cfg;
4573     }
4574     if (this.url) {
4575         this.load();
4576     }
4577     
4578 };
4579 Roo.Template.prototype = {
4580     
4581     /**
4582      * @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..
4583      *                    it should be fixed so that template is observable...
4584      */
4585     url : false,
4586     /**
4587      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4588      */
4589     html : '',
4590     /**
4591      * Returns an HTML fragment of this template with the specified values applied.
4592      * @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'})
4593      * @return {String} The HTML fragment
4594      */
4595     applyTemplate : function(values){
4596         try {
4597            
4598             if(this.compiled){
4599                 return this.compiled(values);
4600             }
4601             var useF = this.disableFormats !== true;
4602             var fm = Roo.util.Format, tpl = this;
4603             var fn = function(m, name, format, args){
4604                 if(format && useF){
4605                     if(format.substr(0, 5) == "this."){
4606                         return tpl.call(format.substr(5), values[name], values);
4607                     }else{
4608                         if(args){
4609                             // quoted values are required for strings in compiled templates, 
4610                             // but for non compiled we need to strip them
4611                             // quoted reversed for jsmin
4612                             var re = /^\s*['"](.*)["']\s*$/;
4613                             args = args.split(',');
4614                             for(var i = 0, len = args.length; i < len; i++){
4615                                 args[i] = args[i].replace(re, "$1");
4616                             }
4617                             args = [values[name]].concat(args);
4618                         }else{
4619                             args = [values[name]];
4620                         }
4621                         return fm[format].apply(fm, args);
4622                     }
4623                 }else{
4624                     return values[name] !== undefined ? values[name] : "";
4625                 }
4626             };
4627             return this.html.replace(this.re, fn);
4628         } catch (e) {
4629             Roo.log(e);
4630             throw e;
4631         }
4632          
4633     },
4634     
4635     loading : false,
4636       
4637     load : function ()
4638     {
4639          
4640         if (this.loading) {
4641             return;
4642         }
4643         var _t = this;
4644         
4645         this.loading = true;
4646         this.compiled = false;
4647         
4648         var cx = new Roo.data.Connection();
4649         cx.request({
4650             url : this.url,
4651             method : 'GET',
4652             success : function (response) {
4653                 _t.loading = false;
4654                 _t.html = response.responseText;
4655                 _t.url = false;
4656                 _t.compile();
4657              },
4658             failure : function(response) {
4659                 Roo.log("Template failed to load from " + _t.url);
4660                 _t.loading = false;
4661             }
4662         });
4663     },
4664
4665     /**
4666      * Sets the HTML used as the template and optionally compiles it.
4667      * @param {String} html
4668      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4669      * @return {Roo.Template} this
4670      */
4671     set : function(html, compile){
4672         this.html = html;
4673         this.compiled = null;
4674         if(compile){
4675             this.compile();
4676         }
4677         return this;
4678     },
4679     
4680     /**
4681      * True to disable format functions (defaults to false)
4682      * @type Boolean
4683      */
4684     disableFormats : false,
4685     
4686     /**
4687     * The regular expression used to match template variables 
4688     * @type RegExp
4689     * @property 
4690     */
4691     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4692     
4693     /**
4694      * Compiles the template into an internal function, eliminating the RegEx overhead.
4695      * @return {Roo.Template} this
4696      */
4697     compile : function(){
4698         var fm = Roo.util.Format;
4699         var useF = this.disableFormats !== true;
4700         var sep = Roo.isGecko ? "+" : ",";
4701         var fn = function(m, name, format, args){
4702             if(format && useF){
4703                 args = args ? ',' + args : "";
4704                 if(format.substr(0, 5) != "this."){
4705                     format = "fm." + format + '(';
4706                 }else{
4707                     format = 'this.call("'+ format.substr(5) + '", ';
4708                     args = ", values";
4709                 }
4710             }else{
4711                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4712             }
4713             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4714         };
4715         var body;
4716         // branched to use + in gecko and [].join() in others
4717         if(Roo.isGecko){
4718             body = "this.compiled = function(values){ return '" +
4719                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4720                     "';};";
4721         }else{
4722             body = ["this.compiled = function(values){ return ['"];
4723             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4724             body.push("'].join('');};");
4725             body = body.join('');
4726         }
4727         /**
4728          * eval:var:values
4729          * eval:var:fm
4730          */
4731         eval(body);
4732         return this;
4733     },
4734     
4735     // private function used to call members
4736     call : function(fnName, value, allValues){
4737         return this[fnName](value, allValues);
4738     },
4739     
4740     /**
4741      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4742      * @param {String/HTMLElement/Roo.Element} el The context element
4743      * @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'})
4744      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4745      * @return {HTMLElement/Roo.Element} The new node or Element
4746      */
4747     insertFirst: function(el, values, returnElement){
4748         return this.doInsert('afterBegin', el, values, returnElement);
4749     },
4750
4751     /**
4752      * Applies the supplied values to the template and inserts the new node(s) before el.
4753      * @param {String/HTMLElement/Roo.Element} el The context element
4754      * @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'})
4755      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4756      * @return {HTMLElement/Roo.Element} The new node or Element
4757      */
4758     insertBefore: function(el, values, returnElement){
4759         return this.doInsert('beforeBegin', el, values, returnElement);
4760     },
4761
4762     /**
4763      * Applies the supplied values to the template and inserts the new node(s) after el.
4764      * @param {String/HTMLElement/Roo.Element} el The context element
4765      * @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'})
4766      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4767      * @return {HTMLElement/Roo.Element} The new node or Element
4768      */
4769     insertAfter : function(el, values, returnElement){
4770         return this.doInsert('afterEnd', el, values, returnElement);
4771     },
4772     
4773     /**
4774      * Applies the supplied values to the template and appends the new node(s) to el.
4775      * @param {String/HTMLElement/Roo.Element} el The context element
4776      * @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'})
4777      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4778      * @return {HTMLElement/Roo.Element} The new node or Element
4779      */
4780     append : function(el, values, returnElement){
4781         return this.doInsert('beforeEnd', el, values, returnElement);
4782     },
4783
4784     doInsert : function(where, el, values, returnEl){
4785         el = Roo.getDom(el);
4786         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4787         return returnEl ? Roo.get(newNode, true) : newNode;
4788     },
4789
4790     /**
4791      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4792      * @param {String/HTMLElement/Roo.Element} el The context element
4793      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4794      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4795      * @return {HTMLElement/Roo.Element} The new node or Element
4796      */
4797     overwrite : function(el, values, returnElement){
4798         el = Roo.getDom(el);
4799         el.innerHTML = this.applyTemplate(values);
4800         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4801     }
4802 };
4803 /**
4804  * Alias for {@link #applyTemplate}
4805  * @method
4806  */
4807 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4808
4809 // backwards compat
4810 Roo.DomHelper.Template = Roo.Template;
4811
4812 /**
4813  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4814  * @param {String/HTMLElement} el A DOM element or its id
4815  * @returns {Roo.Template} The created template
4816  * @static
4817  */
4818 Roo.Template.from = function(el){
4819     el = Roo.getDom(el);
4820     return new Roo.Template(el.value || el.innerHTML);
4821 };/*
4822  * Based on:
4823  * Ext JS Library 1.1.1
4824  * Copyright(c) 2006-2007, Ext JS, LLC.
4825  *
4826  * Originally Released Under LGPL - original licence link has changed is not relivant.
4827  *
4828  * Fork - LGPL
4829  * <script type="text/javascript">
4830  */
4831  
4832
4833 /*
4834  * This is code is also distributed under MIT license for use
4835  * with jQuery and prototype JavaScript libraries.
4836  */
4837 /**
4838  * @class Roo.DomQuery
4839 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).
4840 <p>
4841 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>
4842
4843 <p>
4844 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.
4845 </p>
4846 <h4>Element Selectors:</h4>
4847 <ul class="list">
4848     <li> <b>*</b> any element</li>
4849     <li> <b>E</b> an element with the tag E</li>
4850     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4851     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4852     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4853     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4854 </ul>
4855 <h4>Attribute Selectors:</h4>
4856 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4857 <ul class="list">
4858     <li> <b>E[foo]</b> has an attribute "foo"</li>
4859     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4860     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4861     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4862     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4863     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4864     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4865 </ul>
4866 <h4>Pseudo Classes:</h4>
4867 <ul class="list">
4868     <li> <b>E:first-child</b> E is the first child of its parent</li>
4869     <li> <b>E:last-child</b> E is the last child of its parent</li>
4870     <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>
4871     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4872     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4873     <li> <b>E:only-child</b> E is the only child of its parent</li>
4874     <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>
4875     <li> <b>E:first</b> the first E in the resultset</li>
4876     <li> <b>E:last</b> the last E in the resultset</li>
4877     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4878     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4879     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4880     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4881     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4882     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4883     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4884     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4885     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4886 </ul>
4887 <h4>CSS Value Selectors:</h4>
4888 <ul class="list">
4889     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4890     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4891     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4892     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4893     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4894     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4895 </ul>
4896  * @singleton
4897  */
4898 Roo.DomQuery = function(){
4899     var cache = {}, simpleCache = {}, valueCache = {};
4900     var nonSpace = /\S/;
4901     var trimRe = /^\s+|\s+$/g;
4902     var tplRe = /\{(\d+)\}/g;
4903     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4904     var tagTokenRe = /^(#)?([\w-\*]+)/;
4905     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4906
4907     function child(p, index){
4908         var i = 0;
4909         var n = p.firstChild;
4910         while(n){
4911             if(n.nodeType == 1){
4912                if(++i == index){
4913                    return n;
4914                }
4915             }
4916             n = n.nextSibling;
4917         }
4918         return null;
4919     };
4920
4921     function next(n){
4922         while((n = n.nextSibling) && n.nodeType != 1);
4923         return n;
4924     };
4925
4926     function prev(n){
4927         while((n = n.previousSibling) && n.nodeType != 1);
4928         return n;
4929     };
4930
4931     function children(d){
4932         var n = d.firstChild, ni = -1;
4933             while(n){
4934                 var nx = n.nextSibling;
4935                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4936                     d.removeChild(n);
4937                 }else{
4938                     n.nodeIndex = ++ni;
4939                 }
4940                 n = nx;
4941             }
4942             return this;
4943         };
4944
4945     function byClassName(c, a, v){
4946         if(!v){
4947             return c;
4948         }
4949         var r = [], ri = -1, cn;
4950         for(var i = 0, ci; ci = c[i]; i++){
4951             if((' '+ci.className+' ').indexOf(v) != -1){
4952                 r[++ri] = ci;
4953             }
4954         }
4955         return r;
4956     };
4957
4958     function attrValue(n, attr){
4959         if(!n.tagName && typeof n.length != "undefined"){
4960             n = n[0];
4961         }
4962         if(!n){
4963             return null;
4964         }
4965         if(attr == "for"){
4966             return n.htmlFor;
4967         }
4968         if(attr == "class" || attr == "className"){
4969             return n.className;
4970         }
4971         return n.getAttribute(attr) || n[attr];
4972
4973     };
4974
4975     function getNodes(ns, mode, tagName){
4976         var result = [], ri = -1, cs;
4977         if(!ns){
4978             return result;
4979         }
4980         tagName = tagName || "*";
4981         if(typeof ns.getElementsByTagName != "undefined"){
4982             ns = [ns];
4983         }
4984         if(!mode){
4985             for(var i = 0, ni; ni = ns[i]; i++){
4986                 cs = ni.getElementsByTagName(tagName);
4987                 for(var j = 0, ci; ci = cs[j]; j++){
4988                     result[++ri] = ci;
4989                 }
4990             }
4991         }else if(mode == "/" || mode == ">"){
4992             var utag = tagName.toUpperCase();
4993             for(var i = 0, ni, cn; ni = ns[i]; i++){
4994                 cn = ni.children || ni.childNodes;
4995                 for(var j = 0, cj; cj = cn[j]; j++){
4996                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4997                         result[++ri] = cj;
4998                     }
4999                 }
5000             }
5001         }else if(mode == "+"){
5002             var utag = tagName.toUpperCase();
5003             for(var i = 0, n; n = ns[i]; i++){
5004                 while((n = n.nextSibling) && n.nodeType != 1);
5005                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5006                     result[++ri] = n;
5007                 }
5008             }
5009         }else if(mode == "~"){
5010             for(var i = 0, n; n = ns[i]; i++){
5011                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5012                 if(n){
5013                     result[++ri] = n;
5014                 }
5015             }
5016         }
5017         return result;
5018     };
5019
5020     function concat(a, b){
5021         if(b.slice){
5022             return a.concat(b);
5023         }
5024         for(var i = 0, l = b.length; i < l; i++){
5025             a[a.length] = b[i];
5026         }
5027         return a;
5028     }
5029
5030     function byTag(cs, tagName){
5031         if(cs.tagName || cs == document){
5032             cs = [cs];
5033         }
5034         if(!tagName){
5035             return cs;
5036         }
5037         var r = [], ri = -1;
5038         tagName = tagName.toLowerCase();
5039         for(var i = 0, ci; ci = cs[i]; i++){
5040             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5041                 r[++ri] = ci;
5042             }
5043         }
5044         return r;
5045     };
5046
5047     function byId(cs, attr, id){
5048         if(cs.tagName || cs == document){
5049             cs = [cs];
5050         }
5051         if(!id){
5052             return cs;
5053         }
5054         var r = [], ri = -1;
5055         for(var i = 0,ci; ci = cs[i]; i++){
5056             if(ci && ci.id == id){
5057                 r[++ri] = ci;
5058                 return r;
5059             }
5060         }
5061         return r;
5062     };
5063
5064     function byAttribute(cs, attr, value, op, custom){
5065         var r = [], ri = -1, st = custom=="{";
5066         var f = Roo.DomQuery.operators[op];
5067         for(var i = 0, ci; ci = cs[i]; i++){
5068             var a;
5069             if(st){
5070                 a = Roo.DomQuery.getStyle(ci, attr);
5071             }
5072             else if(attr == "class" || attr == "className"){
5073                 a = ci.className;
5074             }else if(attr == "for"){
5075                 a = ci.htmlFor;
5076             }else if(attr == "href"){
5077                 a = ci.getAttribute("href", 2);
5078             }else{
5079                 a = ci.getAttribute(attr);
5080             }
5081             if((f && f(a, value)) || (!f && a)){
5082                 r[++ri] = ci;
5083             }
5084         }
5085         return r;
5086     };
5087
5088     function byPseudo(cs, name, value){
5089         return Roo.DomQuery.pseudos[name](cs, value);
5090     };
5091
5092     // This is for IE MSXML which does not support expandos.
5093     // IE runs the same speed using setAttribute, however FF slows way down
5094     // and Safari completely fails so they need to continue to use expandos.
5095     var isIE = window.ActiveXObject ? true : false;
5096
5097     // this eval is stop the compressor from
5098     // renaming the variable to something shorter
5099     
5100     /** eval:var:batch */
5101     var batch = 30803; 
5102
5103     var key = 30803;
5104
5105     function nodupIEXml(cs){
5106         var d = ++key;
5107         cs[0].setAttribute("_nodup", d);
5108         var r = [cs[0]];
5109         for(var i = 1, len = cs.length; i < len; i++){
5110             var c = cs[i];
5111             if(!c.getAttribute("_nodup") != d){
5112                 c.setAttribute("_nodup", d);
5113                 r[r.length] = c;
5114             }
5115         }
5116         for(var i = 0, len = cs.length; i < len; i++){
5117             cs[i].removeAttribute("_nodup");
5118         }
5119         return r;
5120     }
5121
5122     function nodup(cs){
5123         if(!cs){
5124             return [];
5125         }
5126         var len = cs.length, c, i, r = cs, cj, ri = -1;
5127         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5128             return cs;
5129         }
5130         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5131             return nodupIEXml(cs);
5132         }
5133         var d = ++key;
5134         cs[0]._nodup = d;
5135         for(i = 1; c = cs[i]; i++){
5136             if(c._nodup != d){
5137                 c._nodup = d;
5138             }else{
5139                 r = [];
5140                 for(var j = 0; j < i; j++){
5141                     r[++ri] = cs[j];
5142                 }
5143                 for(j = i+1; cj = cs[j]; j++){
5144                     if(cj._nodup != d){
5145                         cj._nodup = d;
5146                         r[++ri] = cj;
5147                     }
5148                 }
5149                 return r;
5150             }
5151         }
5152         return r;
5153     }
5154
5155     function quickDiffIEXml(c1, c2){
5156         var d = ++key;
5157         for(var i = 0, len = c1.length; i < len; i++){
5158             c1[i].setAttribute("_qdiff", d);
5159         }
5160         var r = [];
5161         for(var i = 0, len = c2.length; i < len; i++){
5162             if(c2[i].getAttribute("_qdiff") != d){
5163                 r[r.length] = c2[i];
5164             }
5165         }
5166         for(var i = 0, len = c1.length; i < len; i++){
5167            c1[i].removeAttribute("_qdiff");
5168         }
5169         return r;
5170     }
5171
5172     function quickDiff(c1, c2){
5173         var len1 = c1.length;
5174         if(!len1){
5175             return c2;
5176         }
5177         if(isIE && c1[0].selectSingleNode){
5178             return quickDiffIEXml(c1, c2);
5179         }
5180         var d = ++key;
5181         for(var i = 0; i < len1; i++){
5182             c1[i]._qdiff = d;
5183         }
5184         var r = [];
5185         for(var i = 0, len = c2.length; i < len; i++){
5186             if(c2[i]._qdiff != d){
5187                 r[r.length] = c2[i];
5188             }
5189         }
5190         return r;
5191     }
5192
5193     function quickId(ns, mode, root, id){
5194         if(ns == root){
5195            var d = root.ownerDocument || root;
5196            return d.getElementById(id);
5197         }
5198         ns = getNodes(ns, mode, "*");
5199         return byId(ns, null, id);
5200     }
5201
5202     return {
5203         getStyle : function(el, name){
5204             return Roo.fly(el).getStyle(name);
5205         },
5206         /**
5207          * Compiles a selector/xpath query into a reusable function. The returned function
5208          * takes one parameter "root" (optional), which is the context node from where the query should start.
5209          * @param {String} selector The selector/xpath query
5210          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5211          * @return {Function}
5212          */
5213         compile : function(path, type){
5214             type = type || "select";
5215             
5216             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5217             var q = path, mode, lq;
5218             var tk = Roo.DomQuery.matchers;
5219             var tklen = tk.length;
5220             var mm;
5221
5222             // accept leading mode switch
5223             var lmode = q.match(modeRe);
5224             if(lmode && lmode[1]){
5225                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5226                 q = q.replace(lmode[1], "");
5227             }
5228             // strip leading slashes
5229             while(path.substr(0, 1)=="/"){
5230                 path = path.substr(1);
5231             }
5232
5233             while(q && lq != q){
5234                 lq = q;
5235                 var tm = q.match(tagTokenRe);
5236                 if(type == "select"){
5237                     if(tm){
5238                         if(tm[1] == "#"){
5239                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5240                         }else{
5241                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5242                         }
5243                         q = q.replace(tm[0], "");
5244                     }else if(q.substr(0, 1) != '@'){
5245                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5246                     }
5247                 }else{
5248                     if(tm){
5249                         if(tm[1] == "#"){
5250                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5251                         }else{
5252                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5253                         }
5254                         q = q.replace(tm[0], "");
5255                     }
5256                 }
5257                 while(!(mm = q.match(modeRe))){
5258                     var matched = false;
5259                     for(var j = 0; j < tklen; j++){
5260                         var t = tk[j];
5261                         var m = q.match(t.re);
5262                         if(m){
5263                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5264                                                     return m[i];
5265                                                 });
5266                             q = q.replace(m[0], "");
5267                             matched = true;
5268                             break;
5269                         }
5270                     }
5271                     // prevent infinite loop on bad selector
5272                     if(!matched){
5273                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5274                     }
5275                 }
5276                 if(mm[1]){
5277                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5278                     q = q.replace(mm[1], "");
5279                 }
5280             }
5281             fn[fn.length] = "return nodup(n);\n}";
5282             
5283              /** 
5284               * list of variables that need from compression as they are used by eval.
5285              *  eval:var:batch 
5286              *  eval:var:nodup
5287              *  eval:var:byTag
5288              *  eval:var:ById
5289              *  eval:var:getNodes
5290              *  eval:var:quickId
5291              *  eval:var:mode
5292              *  eval:var:root
5293              *  eval:var:n
5294              *  eval:var:byClassName
5295              *  eval:var:byPseudo
5296              *  eval:var:byAttribute
5297              *  eval:var:attrValue
5298              * 
5299              **/ 
5300             eval(fn.join(""));
5301             return f;
5302         },
5303
5304         /**
5305          * Selects a group of elements.
5306          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5307          * @param {Node} root (optional) The start of the query (defaults to document).
5308          * @return {Array}
5309          */
5310         select : function(path, root, type){
5311             if(!root || root == document){
5312                 root = document;
5313             }
5314             if(typeof root == "string"){
5315                 root = document.getElementById(root);
5316             }
5317             var paths = path.split(",");
5318             var results = [];
5319             for(var i = 0, len = paths.length; i < len; i++){
5320                 var p = paths[i].replace(trimRe, "");
5321                 if(!cache[p]){
5322                     cache[p] = Roo.DomQuery.compile(p);
5323                     if(!cache[p]){
5324                         throw p + " is not a valid selector";
5325                     }
5326                 }
5327                 var result = cache[p](root);
5328                 if(result && result != document){
5329                     results = results.concat(result);
5330                 }
5331             }
5332             if(paths.length > 1){
5333                 return nodup(results);
5334             }
5335             return results;
5336         },
5337
5338         /**
5339          * Selects a single element.
5340          * @param {String} selector The selector/xpath query
5341          * @param {Node} root (optional) The start of the query (defaults to document).
5342          * @return {Element}
5343          */
5344         selectNode : function(path, root){
5345             return Roo.DomQuery.select(path, root)[0];
5346         },
5347
5348         /**
5349          * Selects the value of a node, optionally replacing null with the defaultValue.
5350          * @param {String} selector The selector/xpath query
5351          * @param {Node} root (optional) The start of the query (defaults to document).
5352          * @param {String} defaultValue
5353          */
5354         selectValue : function(path, root, defaultValue){
5355             path = path.replace(trimRe, "");
5356             if(!valueCache[path]){
5357                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5358             }
5359             var n = valueCache[path](root);
5360             n = n[0] ? n[0] : n;
5361             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5362             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5363         },
5364
5365         /**
5366          * Selects the value of a node, parsing integers and floats.
5367          * @param {String} selector The selector/xpath query
5368          * @param {Node} root (optional) The start of the query (defaults to document).
5369          * @param {Number} defaultValue
5370          * @return {Number}
5371          */
5372         selectNumber : function(path, root, defaultValue){
5373             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5374             return parseFloat(v);
5375         },
5376
5377         /**
5378          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5379          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5380          * @param {String} selector The simple selector to test
5381          * @return {Boolean}
5382          */
5383         is : function(el, ss){
5384             if(typeof el == "string"){
5385                 el = document.getElementById(el);
5386             }
5387             var isArray = (el instanceof Array);
5388             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5389             return isArray ? (result.length == el.length) : (result.length > 0);
5390         },
5391
5392         /**
5393          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5394          * @param {Array} el An array of elements to filter
5395          * @param {String} selector The simple selector to test
5396          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5397          * the selector instead of the ones that match
5398          * @return {Array}
5399          */
5400         filter : function(els, ss, nonMatches){
5401             ss = ss.replace(trimRe, "");
5402             if(!simpleCache[ss]){
5403                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5404             }
5405             var result = simpleCache[ss](els);
5406             return nonMatches ? quickDiff(result, els) : result;
5407         },
5408
5409         /**
5410          * Collection of matching regular expressions and code snippets.
5411          */
5412         matchers : [{
5413                 re: /^\.([\w-]+)/,
5414                 select: 'n = byClassName(n, null, " {1} ");'
5415             }, {
5416                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5417                 select: 'n = byPseudo(n, "{1}", "{2}");'
5418             },{
5419                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5420                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5421             }, {
5422                 re: /^#([\w-]+)/,
5423                 select: 'n = byId(n, null, "{1}");'
5424             },{
5425                 re: /^@([\w-]+)/,
5426                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5427             }
5428         ],
5429
5430         /**
5431          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5432          * 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;.
5433          */
5434         operators : {
5435             "=" : function(a, v){
5436                 return a == v;
5437             },
5438             "!=" : function(a, v){
5439                 return a != v;
5440             },
5441             "^=" : function(a, v){
5442                 return a && a.substr(0, v.length) == v;
5443             },
5444             "$=" : function(a, v){
5445                 return a && a.substr(a.length-v.length) == v;
5446             },
5447             "*=" : function(a, v){
5448                 return a && a.indexOf(v) !== -1;
5449             },
5450             "%=" : function(a, v){
5451                 return (a % v) == 0;
5452             },
5453             "|=" : function(a, v){
5454                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5455             },
5456             "~=" : function(a, v){
5457                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5458             }
5459         },
5460
5461         /**
5462          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5463          * and the argument (if any) supplied in the selector.
5464          */
5465         pseudos : {
5466             "first-child" : function(c){
5467                 var r = [], ri = -1, n;
5468                 for(var i = 0, ci; ci = n = c[i]; i++){
5469                     while((n = n.previousSibling) && n.nodeType != 1);
5470                     if(!n){
5471                         r[++ri] = ci;
5472                     }
5473                 }
5474                 return r;
5475             },
5476
5477             "last-child" : function(c){
5478                 var r = [], ri = -1, n;
5479                 for(var i = 0, ci; ci = n = c[i]; i++){
5480                     while((n = n.nextSibling) && n.nodeType != 1);
5481                     if(!n){
5482                         r[++ri] = ci;
5483                     }
5484                 }
5485                 return r;
5486             },
5487
5488             "nth-child" : function(c, a) {
5489                 var r = [], ri = -1;
5490                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5491                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5492                 for(var i = 0, n; n = c[i]; i++){
5493                     var pn = n.parentNode;
5494                     if (batch != pn._batch) {
5495                         var j = 0;
5496                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5497                             if(cn.nodeType == 1){
5498                                cn.nodeIndex = ++j;
5499                             }
5500                         }
5501                         pn._batch = batch;
5502                     }
5503                     if (f == 1) {
5504                         if (l == 0 || n.nodeIndex == l){
5505                             r[++ri] = n;
5506                         }
5507                     } else if ((n.nodeIndex + l) % f == 0){
5508                         r[++ri] = n;
5509                     }
5510                 }
5511
5512                 return r;
5513             },
5514
5515             "only-child" : function(c){
5516                 var r = [], ri = -1;;
5517                 for(var i = 0, ci; ci = c[i]; i++){
5518                     if(!prev(ci) && !next(ci)){
5519                         r[++ri] = ci;
5520                     }
5521                 }
5522                 return r;
5523             },
5524
5525             "empty" : function(c){
5526                 var r = [], ri = -1;
5527                 for(var i = 0, ci; ci = c[i]; i++){
5528                     var cns = ci.childNodes, j = 0, cn, empty = true;
5529                     while(cn = cns[j]){
5530                         ++j;
5531                         if(cn.nodeType == 1 || cn.nodeType == 3){
5532                             empty = false;
5533                             break;
5534                         }
5535                     }
5536                     if(empty){
5537                         r[++ri] = ci;
5538                     }
5539                 }
5540                 return r;
5541             },
5542
5543             "contains" : function(c, v){
5544                 var r = [], ri = -1;
5545                 for(var i = 0, ci; ci = c[i]; i++){
5546                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5547                         r[++ri] = ci;
5548                     }
5549                 }
5550                 return r;
5551             },
5552
5553             "nodeValue" : function(c, v){
5554                 var r = [], ri = -1;
5555                 for(var i = 0, ci; ci = c[i]; i++){
5556                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5557                         r[++ri] = ci;
5558                     }
5559                 }
5560                 return r;
5561             },
5562
5563             "checked" : function(c){
5564                 var r = [], ri = -1;
5565                 for(var i = 0, ci; ci = c[i]; i++){
5566                     if(ci.checked == true){
5567                         r[++ri] = ci;
5568                     }
5569                 }
5570                 return r;
5571             },
5572
5573             "not" : function(c, ss){
5574                 return Roo.DomQuery.filter(c, ss, true);
5575             },
5576
5577             "odd" : function(c){
5578                 return this["nth-child"](c, "odd");
5579             },
5580
5581             "even" : function(c){
5582                 return this["nth-child"](c, "even");
5583             },
5584
5585             "nth" : function(c, a){
5586                 return c[a-1] || [];
5587             },
5588
5589             "first" : function(c){
5590                 return c[0] || [];
5591             },
5592
5593             "last" : function(c){
5594                 return c[c.length-1] || [];
5595             },
5596
5597             "has" : function(c, ss){
5598                 var s = Roo.DomQuery.select;
5599                 var r = [], ri = -1;
5600                 for(var i = 0, ci; ci = c[i]; i++){
5601                     if(s(ss, ci).length > 0){
5602                         r[++ri] = ci;
5603                     }
5604                 }
5605                 return r;
5606             },
5607
5608             "next" : function(c, ss){
5609                 var is = Roo.DomQuery.is;
5610                 var r = [], ri = -1;
5611                 for(var i = 0, ci; ci = c[i]; i++){
5612                     var n = next(ci);
5613                     if(n && is(n, ss)){
5614                         r[++ri] = ci;
5615                     }
5616                 }
5617                 return r;
5618             },
5619
5620             "prev" : function(c, ss){
5621                 var is = Roo.DomQuery.is;
5622                 var r = [], ri = -1;
5623                 for(var i = 0, ci; ci = c[i]; i++){
5624                     var n = prev(ci);
5625                     if(n && is(n, ss)){
5626                         r[++ri] = ci;
5627                     }
5628                 }
5629                 return r;
5630             }
5631         }
5632     };
5633 }();
5634
5635 /**
5636  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5637  * @param {String} path The selector/xpath query
5638  * @param {Node} root (optional) The start of the query (defaults to document).
5639  * @return {Array}
5640  * @member Roo
5641  * @method query
5642  */
5643 Roo.query = Roo.DomQuery.select;
5644 /*
5645  * Based on:
5646  * Ext JS Library 1.1.1
5647  * Copyright(c) 2006-2007, Ext JS, LLC.
5648  *
5649  * Originally Released Under LGPL - original licence link has changed is not relivant.
5650  *
5651  * Fork - LGPL
5652  * <script type="text/javascript">
5653  */
5654
5655 /**
5656  * @class Roo.util.Observable
5657  * Base class that provides a common interface for publishing events. Subclasses are expected to
5658  * to have a property "events" with all the events defined.<br>
5659  * For example:
5660  * <pre><code>
5661  Employee = function(name){
5662     this.name = name;
5663     this.addEvents({
5664         "fired" : true,
5665         "quit" : true
5666     });
5667  }
5668  Roo.extend(Employee, Roo.util.Observable);
5669 </code></pre>
5670  * @param {Object} config properties to use (incuding events / listeners)
5671  */
5672
5673 Roo.util.Observable = function(cfg){
5674     
5675     cfg = cfg|| {};
5676     this.addEvents(cfg.events || {});
5677     if (cfg.events) {
5678         delete cfg.events; // make sure
5679     }
5680      
5681     Roo.apply(this, cfg);
5682     
5683     if(this.listeners){
5684         this.on(this.listeners);
5685         delete this.listeners;
5686     }
5687 };
5688 Roo.util.Observable.prototype = {
5689     /** 
5690  * @cfg {Object} listeners  list of events and functions to call for this object, 
5691  * For example :
5692  * <pre><code>
5693     listeners :  { 
5694        'click' : function(e) {
5695            ..... 
5696         } ,
5697         .... 
5698     } 
5699   </code></pre>
5700  */
5701     
5702     
5703     /**
5704      * Fires the specified event with the passed parameters (minus the event name).
5705      * @param {String} eventName
5706      * @param {Object...} args Variable number of parameters are passed to handlers
5707      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5708      */
5709     fireEvent : function(){
5710         var ce = this.events[arguments[0].toLowerCase()];
5711         if(typeof ce == "object"){
5712             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5713         }else{
5714             return true;
5715         }
5716     },
5717
5718     // private
5719     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5720
5721     /**
5722      * Appends an event handler to this component
5723      * @param {String}   eventName The type of event to listen for
5724      * @param {Function} handler The method the event invokes
5725      * @param {Object}   scope (optional) The scope in which to execute the handler
5726      * function. The handler function's "this" context.
5727      * @param {Object}   options (optional) An object containing handler configuration
5728      * properties. This may contain any of the following properties:<ul>
5729      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5730      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5731      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5732      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5733      * by the specified number of milliseconds. If the event fires again within that time, the original
5734      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5735      * </ul><br>
5736      * <p>
5737      * <b>Combining Options</b><br>
5738      * Using the options argument, it is possible to combine different types of listeners:<br>
5739      * <br>
5740      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5741                 <pre><code>
5742                 el.on('click', this.onClick, this, {
5743                         single: true,
5744                 delay: 100,
5745                 forumId: 4
5746                 });
5747                 </code></pre>
5748      * <p>
5749      * <b>Attaching multiple handlers in 1 call</b><br>
5750      * The method also allows for a single argument to be passed which is a config object containing properties
5751      * which specify multiple handlers.
5752      * <pre><code>
5753                 el.on({
5754                         'click': {
5755                         fn: this.onClick,
5756                         scope: this,
5757                         delay: 100
5758                 }, 
5759                 'mouseover': {
5760                         fn: this.onMouseOver,
5761                         scope: this
5762                 },
5763                 'mouseout': {
5764                         fn: this.onMouseOut,
5765                         scope: this
5766                 }
5767                 });
5768                 </code></pre>
5769      * <p>
5770      * Or a shorthand syntax which passes the same scope object to all handlers:
5771         <pre><code>
5772                 el.on({
5773                         'click': this.onClick,
5774                 'mouseover': this.onMouseOver,
5775                 'mouseout': this.onMouseOut,
5776                 scope: this
5777                 });
5778                 </code></pre>
5779      */
5780     addListener : function(eventName, fn, scope, o){
5781         if(typeof eventName == "object"){
5782             o = eventName;
5783             for(var e in o){
5784                 if(this.filterOptRe.test(e)){
5785                     continue;
5786                 }
5787                 if(typeof o[e] == "function"){
5788                     // shared options
5789                     this.addListener(e, o[e], o.scope,  o);
5790                 }else{
5791                     // individual options
5792                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5793                 }
5794             }
5795             return;
5796         }
5797         o = (!o || typeof o == "boolean") ? {} : o;
5798         eventName = eventName.toLowerCase();
5799         var ce = this.events[eventName] || true;
5800         if(typeof ce == "boolean"){
5801             ce = new Roo.util.Event(this, eventName);
5802             this.events[eventName] = ce;
5803         }
5804         ce.addListener(fn, scope, o);
5805     },
5806
5807     /**
5808      * Removes a listener
5809      * @param {String}   eventName     The type of event to listen for
5810      * @param {Function} handler        The handler to remove
5811      * @param {Object}   scope  (optional) The scope (this object) for the handler
5812      */
5813     removeListener : function(eventName, fn, scope){
5814         var ce = this.events[eventName.toLowerCase()];
5815         if(typeof ce == "object"){
5816             ce.removeListener(fn, scope);
5817         }
5818     },
5819
5820     /**
5821      * Removes all listeners for this object
5822      */
5823     purgeListeners : function(){
5824         for(var evt in this.events){
5825             if(typeof this.events[evt] == "object"){
5826                  this.events[evt].clearListeners();
5827             }
5828         }
5829     },
5830
5831     relayEvents : function(o, events){
5832         var createHandler = function(ename){
5833             return function(){
5834                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5835             };
5836         };
5837         for(var i = 0, len = events.length; i < len; i++){
5838             var ename = events[i];
5839             if(!this.events[ename]){ this.events[ename] = true; };
5840             o.on(ename, createHandler(ename), this);
5841         }
5842     },
5843
5844     /**
5845      * Used to define events on this Observable
5846      * @param {Object} object The object with the events defined
5847      */
5848     addEvents : function(o){
5849         if(!this.events){
5850             this.events = {};
5851         }
5852         Roo.applyIf(this.events, o);
5853     },
5854
5855     /**
5856      * Checks to see if this object has any listeners for a specified event
5857      * @param {String} eventName The name of the event to check for
5858      * @return {Boolean} True if the event is being listened for, else false
5859      */
5860     hasListener : function(eventName){
5861         var e = this.events[eventName];
5862         return typeof e == "object" && e.listeners.length > 0;
5863     }
5864 };
5865 /**
5866  * Appends an event handler to this element (shorthand for addListener)
5867  * @param {String}   eventName     The type of event to listen for
5868  * @param {Function} handler        The method the event invokes
5869  * @param {Object}   scope (optional) The scope in which to execute the handler
5870  * function. The handler function's "this" context.
5871  * @param {Object}   options  (optional)
5872  * @method
5873  */
5874 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5875 /**
5876  * Removes a listener (shorthand for removeListener)
5877  * @param {String}   eventName     The type of event to listen for
5878  * @param {Function} handler        The handler to remove
5879  * @param {Object}   scope  (optional) The scope (this object) for the handler
5880  * @method
5881  */
5882 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5883
5884 /**
5885  * Starts capture on the specified Observable. All events will be passed
5886  * to the supplied function with the event name + standard signature of the event
5887  * <b>before</b> the event is fired. If the supplied function returns false,
5888  * the event will not fire.
5889  * @param {Observable} o The Observable to capture
5890  * @param {Function} fn The function to call
5891  * @param {Object} scope (optional) The scope (this object) for the fn
5892  * @static
5893  */
5894 Roo.util.Observable.capture = function(o, fn, scope){
5895     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5896 };
5897
5898 /**
5899  * Removes <b>all</b> added captures from the Observable.
5900  * @param {Observable} o The Observable to release
5901  * @static
5902  */
5903 Roo.util.Observable.releaseCapture = function(o){
5904     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5905 };
5906
5907 (function(){
5908
5909     var createBuffered = function(h, o, scope){
5910         var task = new Roo.util.DelayedTask();
5911         return function(){
5912             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5913         };
5914     };
5915
5916     var createSingle = function(h, e, fn, scope){
5917         return function(){
5918             e.removeListener(fn, scope);
5919             return h.apply(scope, arguments);
5920         };
5921     };
5922
5923     var createDelayed = function(h, o, scope){
5924         return function(){
5925             var args = Array.prototype.slice.call(arguments, 0);
5926             setTimeout(function(){
5927                 h.apply(scope, args);
5928             }, o.delay || 10);
5929         };
5930     };
5931
5932     Roo.util.Event = function(obj, name){
5933         this.name = name;
5934         this.obj = obj;
5935         this.listeners = [];
5936     };
5937
5938     Roo.util.Event.prototype = {
5939         addListener : function(fn, scope, options){
5940             var o = options || {};
5941             scope = scope || this.obj;
5942             if(!this.isListening(fn, scope)){
5943                 var l = {fn: fn, scope: scope, options: o};
5944                 var h = fn;
5945                 if(o.delay){
5946                     h = createDelayed(h, o, scope);
5947                 }
5948                 if(o.single){
5949                     h = createSingle(h, this, fn, scope);
5950                 }
5951                 if(o.buffer){
5952                     h = createBuffered(h, o, scope);
5953                 }
5954                 l.fireFn = h;
5955                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5956                     this.listeners.push(l);
5957                 }else{
5958                     this.listeners = this.listeners.slice(0);
5959                     this.listeners.push(l);
5960                 }
5961             }
5962         },
5963
5964         findListener : function(fn, scope){
5965             scope = scope || this.obj;
5966             var ls = this.listeners;
5967             for(var i = 0, len = ls.length; i < len; i++){
5968                 var l = ls[i];
5969                 if(l.fn == fn && l.scope == scope){
5970                     return i;
5971                 }
5972             }
5973             return -1;
5974         },
5975
5976         isListening : function(fn, scope){
5977             return this.findListener(fn, scope) != -1;
5978         },
5979
5980         removeListener : function(fn, scope){
5981             var index;
5982             if((index = this.findListener(fn, scope)) != -1){
5983                 if(!this.firing){
5984                     this.listeners.splice(index, 1);
5985                 }else{
5986                     this.listeners = this.listeners.slice(0);
5987                     this.listeners.splice(index, 1);
5988                 }
5989                 return true;
5990             }
5991             return false;
5992         },
5993
5994         clearListeners : function(){
5995             this.listeners = [];
5996         },
5997
5998         fire : function(){
5999             var ls = this.listeners, scope, len = ls.length;
6000             if(len > 0){
6001                 this.firing = true;
6002                 var args = Array.prototype.slice.call(arguments, 0);
6003                 for(var i = 0; i < len; i++){
6004                     var l = ls[i];
6005                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6006                         this.firing = false;
6007                         return false;
6008                     }
6009                 }
6010                 this.firing = false;
6011             }
6012             return true;
6013         }
6014     };
6015 })();/*
6016  * Based on:
6017  * Ext JS Library 1.1.1
6018  * Copyright(c) 2006-2007, Ext JS, LLC.
6019  *
6020  * Originally Released Under LGPL - original licence link has changed is not relivant.
6021  *
6022  * Fork - LGPL
6023  * <script type="text/javascript">
6024  */
6025
6026 /**
6027  * @class Roo.EventManager
6028  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6029  * several useful events directly.
6030  * See {@link Roo.EventObject} for more details on normalized event objects.
6031  * @singleton
6032  */
6033 Roo.EventManager = function(){
6034     var docReadyEvent, docReadyProcId, docReadyState = false;
6035     var resizeEvent, resizeTask, textEvent, textSize;
6036     var E = Roo.lib.Event;
6037     var D = Roo.lib.Dom;
6038
6039
6040     var fireDocReady = function(){
6041         if(!docReadyState){
6042             docReadyState = true;
6043             Roo.isReady = true;
6044             if(docReadyProcId){
6045                 clearInterval(docReadyProcId);
6046             }
6047             if(Roo.isGecko || Roo.isOpera) {
6048                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6049             }
6050             if(Roo.isIE){
6051                 var defer = document.getElementById("ie-deferred-loader");
6052                 if(defer){
6053                     defer.onreadystatechange = null;
6054                     defer.parentNode.removeChild(defer);
6055                 }
6056             }
6057             if(docReadyEvent){
6058                 docReadyEvent.fire();
6059                 docReadyEvent.clearListeners();
6060             }
6061         }
6062     };
6063     
6064     var initDocReady = function(){
6065         docReadyEvent = new Roo.util.Event();
6066         if(Roo.isGecko || Roo.isOpera) {
6067             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6068         }else if(Roo.isIE){
6069             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6070             var defer = document.getElementById("ie-deferred-loader");
6071             defer.onreadystatechange = function(){
6072                 if(this.readyState == "complete"){
6073                     fireDocReady();
6074                 }
6075             };
6076         }else if(Roo.isSafari){ 
6077             docReadyProcId = setInterval(function(){
6078                 var rs = document.readyState;
6079                 if(rs == "complete") {
6080                     fireDocReady();     
6081                  }
6082             }, 10);
6083         }
6084         // no matter what, make sure it fires on load
6085         E.on(window, "load", fireDocReady);
6086     };
6087
6088     var createBuffered = function(h, o){
6089         var task = new Roo.util.DelayedTask(h);
6090         return function(e){
6091             // create new event object impl so new events don't wipe out properties
6092             e = new Roo.EventObjectImpl(e);
6093             task.delay(o.buffer, h, null, [e]);
6094         };
6095     };
6096
6097     var createSingle = function(h, el, ename, fn){
6098         return function(e){
6099             Roo.EventManager.removeListener(el, ename, fn);
6100             h(e);
6101         };
6102     };
6103
6104     var createDelayed = function(h, o){
6105         return function(e){
6106             // create new event object impl so new events don't wipe out properties
6107             e = new Roo.EventObjectImpl(e);
6108             setTimeout(function(){
6109                 h(e);
6110             }, o.delay || 10);
6111         };
6112     };
6113
6114     var listen = function(element, ename, opt, fn, scope){
6115         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6116         fn = fn || o.fn; scope = scope || o.scope;
6117         var el = Roo.getDom(element);
6118         if(!el){
6119             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6120         }
6121         var h = function(e){
6122             e = Roo.EventObject.setEvent(e);
6123             var t;
6124             if(o.delegate){
6125                 t = e.getTarget(o.delegate, el);
6126                 if(!t){
6127                     return;
6128                 }
6129             }else{
6130                 t = e.target;
6131             }
6132             if(o.stopEvent === true){
6133                 e.stopEvent();
6134             }
6135             if(o.preventDefault === true){
6136                e.preventDefault();
6137             }
6138             if(o.stopPropagation === true){
6139                 e.stopPropagation();
6140             }
6141
6142             if(o.normalized === false){
6143                 e = e.browserEvent;
6144             }
6145
6146             fn.call(scope || el, e, t, o);
6147         };
6148         if(o.delay){
6149             h = createDelayed(h, o);
6150         }
6151         if(o.single){
6152             h = createSingle(h, el, ename, fn);
6153         }
6154         if(o.buffer){
6155             h = createBuffered(h, o);
6156         }
6157         fn._handlers = fn._handlers || [];
6158         fn._handlers.push([Roo.id(el), ename, h]);
6159
6160         E.on(el, ename, h);
6161         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6162             el.addEventListener("DOMMouseScroll", h, false);
6163             E.on(window, 'unload', function(){
6164                 el.removeEventListener("DOMMouseScroll", h, false);
6165             });
6166         }
6167         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6168             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6169         }
6170         return h;
6171     };
6172
6173     var stopListening = function(el, ename, fn){
6174         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6175         if(hds){
6176             for(var i = 0, len = hds.length; i < len; i++){
6177                 var h = hds[i];
6178                 if(h[0] == id && h[1] == ename){
6179                     hd = h[2];
6180                     hds.splice(i, 1);
6181                     break;
6182                 }
6183             }
6184         }
6185         E.un(el, ename, hd);
6186         el = Roo.getDom(el);
6187         if(ename == "mousewheel" && el.addEventListener){
6188             el.removeEventListener("DOMMouseScroll", hd, false);
6189         }
6190         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6191             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6192         }
6193     };
6194
6195     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6196     
6197     var pub = {
6198         
6199         
6200         /** 
6201          * Fix for doc tools
6202          * @scope Roo.EventManager
6203          */
6204         
6205         
6206         /** 
6207          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6208          * object with a Roo.EventObject
6209          * @param {Function} fn        The method the event invokes
6210          * @param {Object}   scope    An object that becomes the scope of the handler
6211          * @param {boolean}  override If true, the obj passed in becomes
6212          *                             the execution scope of the listener
6213          * @return {Function} The wrapped function
6214          * @deprecated
6215          */
6216         wrap : function(fn, scope, override){
6217             return function(e){
6218                 Roo.EventObject.setEvent(e);
6219                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6220             };
6221         },
6222         
6223         /**
6224      * Appends an event handler to an element (shorthand for addListener)
6225      * @param {String/HTMLElement}   element        The html element or id to assign the
6226      * @param {String}   eventName The type of event to listen for
6227      * @param {Function} handler The method the event invokes
6228      * @param {Object}   scope (optional) The scope in which to execute the handler
6229      * function. The handler function's "this" context.
6230      * @param {Object}   options (optional) An object containing handler configuration
6231      * properties. This may contain any of the following properties:<ul>
6232      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6233      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6234      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6235      * <li>preventDefault {Boolean} True to prevent the default action</li>
6236      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6237      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6238      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6239      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6240      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6241      * by the specified number of milliseconds. If the event fires again within that time, the original
6242      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6243      * </ul><br>
6244      * <p>
6245      * <b>Combining Options</b><br>
6246      * Using the options argument, it is possible to combine different types of listeners:<br>
6247      * <br>
6248      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6249      * Code:<pre><code>
6250 el.on('click', this.onClick, this, {
6251     single: true,
6252     delay: 100,
6253     stopEvent : true,
6254     forumId: 4
6255 });</code></pre>
6256      * <p>
6257      * <b>Attaching multiple handlers in 1 call</b><br>
6258       * The method also allows for a single argument to be passed which is a config object containing properties
6259      * which specify multiple handlers.
6260      * <p>
6261      * Code:<pre><code>
6262 el.on({
6263     'click' : {
6264         fn: this.onClick
6265         scope: this,
6266         delay: 100
6267     },
6268     'mouseover' : {
6269         fn: this.onMouseOver
6270         scope: this
6271     },
6272     'mouseout' : {
6273         fn: this.onMouseOut
6274         scope: this
6275     }
6276 });</code></pre>
6277      * <p>
6278      * Or a shorthand syntax:<br>
6279      * Code:<pre><code>
6280 el.on({
6281     'click' : this.onClick,
6282     'mouseover' : this.onMouseOver,
6283     'mouseout' : this.onMouseOut
6284     scope: this
6285 });</code></pre>
6286      */
6287         addListener : function(element, eventName, fn, scope, options){
6288             if(typeof eventName == "object"){
6289                 var o = eventName;
6290                 for(var e in o){
6291                     if(propRe.test(e)){
6292                         continue;
6293                     }
6294                     if(typeof o[e] == "function"){
6295                         // shared options
6296                         listen(element, e, o, o[e], o.scope);
6297                     }else{
6298                         // individual options
6299                         listen(element, e, o[e]);
6300                     }
6301                 }
6302                 return;
6303             }
6304             return listen(element, eventName, options, fn, scope);
6305         },
6306         
6307         /**
6308          * Removes an event handler
6309          *
6310          * @param {String/HTMLElement}   element        The id or html element to remove the 
6311          *                             event from
6312          * @param {String}   eventName     The type of event
6313          * @param {Function} fn
6314          * @return {Boolean} True if a listener was actually removed
6315          */
6316         removeListener : function(element, eventName, fn){
6317             return stopListening(element, eventName, fn);
6318         },
6319         
6320         /**
6321          * Fires when the document is ready (before onload and before images are loaded). Can be 
6322          * accessed shorthanded Roo.onReady().
6323          * @param {Function} fn        The method the event invokes
6324          * @param {Object}   scope    An  object that becomes the scope of the handler
6325          * @param {boolean}  options
6326          */
6327         onDocumentReady : function(fn, scope, options){
6328             if(docReadyState){ // if it already fired
6329                 docReadyEvent.addListener(fn, scope, options);
6330                 docReadyEvent.fire();
6331                 docReadyEvent.clearListeners();
6332                 return;
6333             }
6334             if(!docReadyEvent){
6335                 initDocReady();
6336             }
6337             docReadyEvent.addListener(fn, scope, options);
6338         },
6339         
6340         /**
6341          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6342          * @param {Function} fn        The method the event invokes
6343          * @param {Object}   scope    An object that becomes the scope of the handler
6344          * @param {boolean}  options
6345          */
6346         onWindowResize : function(fn, scope, options){
6347             if(!resizeEvent){
6348                 resizeEvent = new Roo.util.Event();
6349                 resizeTask = new Roo.util.DelayedTask(function(){
6350                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6351                 });
6352                 E.on(window, "resize", function(){
6353                     if(Roo.isIE){
6354                         resizeTask.delay(50);
6355                     }else{
6356                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6357                     }
6358                 });
6359             }
6360             resizeEvent.addListener(fn, scope, options);
6361         },
6362
6363         /**
6364          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6365          * @param {Function} fn        The method the event invokes
6366          * @param {Object}   scope    An object that becomes the scope of the handler
6367          * @param {boolean}  options
6368          */
6369         onTextResize : function(fn, scope, options){
6370             if(!textEvent){
6371                 textEvent = new Roo.util.Event();
6372                 var textEl = new Roo.Element(document.createElement('div'));
6373                 textEl.dom.className = 'x-text-resize';
6374                 textEl.dom.innerHTML = 'X';
6375                 textEl.appendTo(document.body);
6376                 textSize = textEl.dom.offsetHeight;
6377                 setInterval(function(){
6378                     if(textEl.dom.offsetHeight != textSize){
6379                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6380                     }
6381                 }, this.textResizeInterval);
6382             }
6383             textEvent.addListener(fn, scope, options);
6384         },
6385
6386         /**
6387          * Removes the passed window resize listener.
6388          * @param {Function} fn        The method the event invokes
6389          * @param {Object}   scope    The scope of handler
6390          */
6391         removeResizeListener : function(fn, scope){
6392             if(resizeEvent){
6393                 resizeEvent.removeListener(fn, scope);
6394             }
6395         },
6396
6397         // private
6398         fireResize : function(){
6399             if(resizeEvent){
6400                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6401             }   
6402         },
6403         /**
6404          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6405          */
6406         ieDeferSrc : false,
6407         /**
6408          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6409          */
6410         textResizeInterval : 50
6411     };
6412     
6413     /**
6414      * Fix for doc tools
6415      * @scopeAlias pub=Roo.EventManager
6416      */
6417     
6418      /**
6419      * Appends an event handler to an element (shorthand for addListener)
6420      * @param {String/HTMLElement}   element        The html element or id to assign the
6421      * @param {String}   eventName The type of event to listen for
6422      * @param {Function} handler The method the event invokes
6423      * @param {Object}   scope (optional) The scope in which to execute the handler
6424      * function. The handler function's "this" context.
6425      * @param {Object}   options (optional) An object containing handler configuration
6426      * properties. This may contain any of the following properties:<ul>
6427      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6428      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6429      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6430      * <li>preventDefault {Boolean} True to prevent the default action</li>
6431      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6432      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6433      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6434      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6435      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6436      * by the specified number of milliseconds. If the event fires again within that time, the original
6437      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6438      * </ul><br>
6439      * <p>
6440      * <b>Combining Options</b><br>
6441      * Using the options argument, it is possible to combine different types of listeners:<br>
6442      * <br>
6443      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6444      * Code:<pre><code>
6445 el.on('click', this.onClick, this, {
6446     single: true,
6447     delay: 100,
6448     stopEvent : true,
6449     forumId: 4
6450 });</code></pre>
6451      * <p>
6452      * <b>Attaching multiple handlers in 1 call</b><br>
6453       * The method also allows for a single argument to be passed which is a config object containing properties
6454      * which specify multiple handlers.
6455      * <p>
6456      * Code:<pre><code>
6457 el.on({
6458     'click' : {
6459         fn: this.onClick
6460         scope: this,
6461         delay: 100
6462     },
6463     'mouseover' : {
6464         fn: this.onMouseOver
6465         scope: this
6466     },
6467     'mouseout' : {
6468         fn: this.onMouseOut
6469         scope: this
6470     }
6471 });</code></pre>
6472      * <p>
6473      * Or a shorthand syntax:<br>
6474      * Code:<pre><code>
6475 el.on({
6476     'click' : this.onClick,
6477     'mouseover' : this.onMouseOver,
6478     'mouseout' : this.onMouseOut
6479     scope: this
6480 });</code></pre>
6481      */
6482     pub.on = pub.addListener;
6483     pub.un = pub.removeListener;
6484
6485     pub.stoppedMouseDownEvent = new Roo.util.Event();
6486     return pub;
6487 }();
6488 /**
6489   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6490   * @param {Function} fn        The method the event invokes
6491   * @param {Object}   scope    An  object that becomes the scope of the handler
6492   * @param {boolean}  override If true, the obj passed in becomes
6493   *                             the execution scope of the listener
6494   * @member Roo
6495   * @method onReady
6496  */
6497 Roo.onReady = Roo.EventManager.onDocumentReady;
6498
6499 Roo.onReady(function(){
6500     var bd = Roo.get(document.body);
6501     if(!bd){ return; }
6502
6503     var cls = [
6504             Roo.isIE ? "roo-ie"
6505             : Roo.isGecko ? "roo-gecko"
6506             : Roo.isOpera ? "roo-opera"
6507             : Roo.isSafari ? "roo-safari" : ""];
6508
6509     if(Roo.isMac){
6510         cls.push("roo-mac");
6511     }
6512     if(Roo.isLinux){
6513         cls.push("roo-linux");
6514     }
6515     if(Roo.isBorderBox){
6516         cls.push('roo-border-box');
6517     }
6518     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6519         var p = bd.dom.parentNode;
6520         if(p){
6521             p.className += ' roo-strict';
6522         }
6523     }
6524     bd.addClass(cls.join(' '));
6525 });
6526
6527 /**
6528  * @class Roo.EventObject
6529  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6530  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6531  * Example:
6532  * <pre><code>
6533  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6534     e.preventDefault();
6535     var target = e.getTarget();
6536     ...
6537  }
6538  var myDiv = Roo.get("myDiv");
6539  myDiv.on("click", handleClick);
6540  //or
6541  Roo.EventManager.on("myDiv", 'click', handleClick);
6542  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6543  </code></pre>
6544  * @singleton
6545  */
6546 Roo.EventObject = function(){
6547     
6548     var E = Roo.lib.Event;
6549     
6550     // safari keypress events for special keys return bad keycodes
6551     var safariKeys = {
6552         63234 : 37, // left
6553         63235 : 39, // right
6554         63232 : 38, // up
6555         63233 : 40, // down
6556         63276 : 33, // page up
6557         63277 : 34, // page down
6558         63272 : 46, // delete
6559         63273 : 36, // home
6560         63275 : 35  // end
6561     };
6562
6563     // normalize button clicks
6564     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6565                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6566
6567     Roo.EventObjectImpl = function(e){
6568         if(e){
6569             this.setEvent(e.browserEvent || e);
6570         }
6571     };
6572     Roo.EventObjectImpl.prototype = {
6573         /**
6574          * Used to fix doc tools.
6575          * @scope Roo.EventObject.prototype
6576          */
6577             
6578
6579         
6580         
6581         /** The normal browser event */
6582         browserEvent : null,
6583         /** The button pressed in a mouse event */
6584         button : -1,
6585         /** True if the shift key was down during the event */
6586         shiftKey : false,
6587         /** True if the control key was down during the event */
6588         ctrlKey : false,
6589         /** True if the alt key was down during the event */
6590         altKey : false,
6591
6592         /** Key constant 
6593         * @type Number */
6594         BACKSPACE : 8,
6595         /** Key constant 
6596         * @type Number */
6597         TAB : 9,
6598         /** Key constant 
6599         * @type Number */
6600         RETURN : 13,
6601         /** Key constant 
6602         * @type Number */
6603         ENTER : 13,
6604         /** Key constant 
6605         * @type Number */
6606         SHIFT : 16,
6607         /** Key constant 
6608         * @type Number */
6609         CONTROL : 17,
6610         /** Key constant 
6611         * @type Number */
6612         ESC : 27,
6613         /** Key constant 
6614         * @type Number */
6615         SPACE : 32,
6616         /** Key constant 
6617         * @type Number */
6618         PAGEUP : 33,
6619         /** Key constant 
6620         * @type Number */
6621         PAGEDOWN : 34,
6622         /** Key constant 
6623         * @type Number */
6624         END : 35,
6625         /** Key constant 
6626         * @type Number */
6627         HOME : 36,
6628         /** Key constant 
6629         * @type Number */
6630         LEFT : 37,
6631         /** Key constant 
6632         * @type Number */
6633         UP : 38,
6634         /** Key constant 
6635         * @type Number */
6636         RIGHT : 39,
6637         /** Key constant 
6638         * @type Number */
6639         DOWN : 40,
6640         /** Key constant 
6641         * @type Number */
6642         DELETE : 46,
6643         /** Key constant 
6644         * @type Number */
6645         F5 : 116,
6646
6647            /** @private */
6648         setEvent : function(e){
6649             if(e == this || (e && e.browserEvent)){ // already wrapped
6650                 return e;
6651             }
6652             this.browserEvent = e;
6653             if(e){
6654                 // normalize buttons
6655                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6656                 if(e.type == 'click' && this.button == -1){
6657                     this.button = 0;
6658                 }
6659                 this.type = e.type;
6660                 this.shiftKey = e.shiftKey;
6661                 // mac metaKey behaves like ctrlKey
6662                 this.ctrlKey = e.ctrlKey || e.metaKey;
6663                 this.altKey = e.altKey;
6664                 // in getKey these will be normalized for the mac
6665                 this.keyCode = e.keyCode;
6666                 // keyup warnings on firefox.
6667                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6668                 // cache the target for the delayed and or buffered events
6669                 this.target = E.getTarget(e);
6670                 // same for XY
6671                 this.xy = E.getXY(e);
6672             }else{
6673                 this.button = -1;
6674                 this.shiftKey = false;
6675                 this.ctrlKey = false;
6676                 this.altKey = false;
6677                 this.keyCode = 0;
6678                 this.charCode =0;
6679                 this.target = null;
6680                 this.xy = [0, 0];
6681             }
6682             return this;
6683         },
6684
6685         /**
6686          * Stop the event (preventDefault and stopPropagation)
6687          */
6688         stopEvent : function(){
6689             if(this.browserEvent){
6690                 if(this.browserEvent.type == 'mousedown'){
6691                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6692                 }
6693                 E.stopEvent(this.browserEvent);
6694             }
6695         },
6696
6697         /**
6698          * Prevents the browsers default handling of the event.
6699          */
6700         preventDefault : function(){
6701             if(this.browserEvent){
6702                 E.preventDefault(this.browserEvent);
6703             }
6704         },
6705
6706         /** @private */
6707         isNavKeyPress : function(){
6708             var k = this.keyCode;
6709             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6710             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6711         },
6712
6713         isSpecialKey : function(){
6714             var k = this.keyCode;
6715             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6716             (k == 16) || (k == 17) ||
6717             (k >= 18 && k <= 20) ||
6718             (k >= 33 && k <= 35) ||
6719             (k >= 36 && k <= 39) ||
6720             (k >= 44 && k <= 45);
6721         },
6722         /**
6723          * Cancels bubbling of the event.
6724          */
6725         stopPropagation : function(){
6726             if(this.browserEvent){
6727                 if(this.type == 'mousedown'){
6728                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6729                 }
6730                 E.stopPropagation(this.browserEvent);
6731             }
6732         },
6733
6734         /**
6735          * Gets the key code for the event.
6736          * @return {Number}
6737          */
6738         getCharCode : function(){
6739             return this.charCode || this.keyCode;
6740         },
6741
6742         /**
6743          * Returns a normalized keyCode for the event.
6744          * @return {Number} The key code
6745          */
6746         getKey : function(){
6747             var k = this.keyCode || this.charCode;
6748             return Roo.isSafari ? (safariKeys[k] || k) : k;
6749         },
6750
6751         /**
6752          * Gets the x coordinate of the event.
6753          * @return {Number}
6754          */
6755         getPageX : function(){
6756             return this.xy[0];
6757         },
6758
6759         /**
6760          * Gets the y coordinate of the event.
6761          * @return {Number}
6762          */
6763         getPageY : function(){
6764             return this.xy[1];
6765         },
6766
6767         /**
6768          * Gets the time of the event.
6769          * @return {Number}
6770          */
6771         getTime : function(){
6772             if(this.browserEvent){
6773                 return E.getTime(this.browserEvent);
6774             }
6775             return null;
6776         },
6777
6778         /**
6779          * Gets the page coordinates of the event.
6780          * @return {Array} The xy values like [x, y]
6781          */
6782         getXY : function(){
6783             return this.xy;
6784         },
6785
6786         /**
6787          * Gets the target for the event.
6788          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6789          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6790                 search as a number or element (defaults to 10 || document.body)
6791          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6792          * @return {HTMLelement}
6793          */
6794         getTarget : function(selector, maxDepth, returnEl){
6795             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6796         },
6797         /**
6798          * Gets the related target.
6799          * @return {HTMLElement}
6800          */
6801         getRelatedTarget : function(){
6802             if(this.browserEvent){
6803                 return E.getRelatedTarget(this.browserEvent);
6804             }
6805             return null;
6806         },
6807
6808         /**
6809          * Normalizes mouse wheel delta across browsers
6810          * @return {Number} The delta
6811          */
6812         getWheelDelta : function(){
6813             var e = this.browserEvent;
6814             var delta = 0;
6815             if(e.wheelDelta){ /* IE/Opera. */
6816                 delta = e.wheelDelta/120;
6817             }else if(e.detail){ /* Mozilla case. */
6818                 delta = -e.detail/3;
6819             }
6820             return delta;
6821         },
6822
6823         /**
6824          * Returns true if the control, meta, shift or alt key was pressed during this event.
6825          * @return {Boolean}
6826          */
6827         hasModifier : function(){
6828             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6829         },
6830
6831         /**
6832          * Returns true if the target of this event equals el or is a child of el
6833          * @param {String/HTMLElement/Element} el
6834          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6835          * @return {Boolean}
6836          */
6837         within : function(el, related){
6838             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6839             return t && Roo.fly(el).contains(t);
6840         },
6841
6842         getPoint : function(){
6843             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6844         }
6845     };
6846
6847     return new Roo.EventObjectImpl();
6848 }();
6849             
6850     /*
6851  * Based on:
6852  * Ext JS Library 1.1.1
6853  * Copyright(c) 2006-2007, Ext JS, LLC.
6854  *
6855  * Originally Released Under LGPL - original licence link has changed is not relivant.
6856  *
6857  * Fork - LGPL
6858  * <script type="text/javascript">
6859  */
6860
6861  
6862 // was in Composite Element!??!?!
6863  
6864 (function(){
6865     var D = Roo.lib.Dom;
6866     var E = Roo.lib.Event;
6867     var A = Roo.lib.Anim;
6868
6869     // local style camelizing for speed
6870     var propCache = {};
6871     var camelRe = /(-[a-z])/gi;
6872     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6873     var view = document.defaultView;
6874
6875 /**
6876  * @class Roo.Element
6877  * Represents an Element in the DOM.<br><br>
6878  * Usage:<br>
6879 <pre><code>
6880 var el = Roo.get("my-div");
6881
6882 // or with getEl
6883 var el = getEl("my-div");
6884
6885 // or with a DOM element
6886 var el = Roo.get(myDivElement);
6887 </code></pre>
6888  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6889  * each call instead of constructing a new one.<br><br>
6890  * <b>Animations</b><br />
6891  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6892  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6893 <pre>
6894 Option    Default   Description
6895 --------- --------  ---------------------------------------------
6896 duration  .35       The duration of the animation in seconds
6897 easing    easeOut   The YUI easing method
6898 callback  none      A function to execute when the anim completes
6899 scope     this      The scope (this) of the callback function
6900 </pre>
6901 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6902 * manipulate the animation. Here's an example:
6903 <pre><code>
6904 var el = Roo.get("my-div");
6905
6906 // no animation
6907 el.setWidth(100);
6908
6909 // default animation
6910 el.setWidth(100, true);
6911
6912 // animation with some options set
6913 el.setWidth(100, {
6914     duration: 1,
6915     callback: this.foo,
6916     scope: this
6917 });
6918
6919 // using the "anim" property to get the Anim object
6920 var opt = {
6921     duration: 1,
6922     callback: this.foo,
6923     scope: this
6924 };
6925 el.setWidth(100, opt);
6926 ...
6927 if(opt.anim.isAnimated()){
6928     opt.anim.stop();
6929 }
6930 </code></pre>
6931 * <b> Composite (Collections of) Elements</b><br />
6932  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6933  * @constructor Create a new Element directly.
6934  * @param {String/HTMLElement} element
6935  * @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).
6936  */
6937     Roo.Element = function(element, forceNew){
6938         var dom = typeof element == "string" ?
6939                 document.getElementById(element) : element;
6940         if(!dom){ // invalid id/element
6941             return null;
6942         }
6943         var id = dom.id;
6944         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6945             return Roo.Element.cache[id];
6946         }
6947
6948         /**
6949          * The DOM element
6950          * @type HTMLElement
6951          */
6952         this.dom = dom;
6953
6954         /**
6955          * The DOM element ID
6956          * @type String
6957          */
6958         this.id = id || Roo.id(dom);
6959     };
6960
6961     var El = Roo.Element;
6962
6963     El.prototype = {
6964         /**
6965          * The element's default display mode  (defaults to "")
6966          * @type String
6967          */
6968         originalDisplay : "",
6969
6970         visibilityMode : 1,
6971         /**
6972          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6973          * @type String
6974          */
6975         defaultUnit : "px",
6976         /**
6977          * Sets the element's visibility mode. When setVisible() is called it
6978          * will use this to determine whether to set the visibility or the display property.
6979          * @param visMode Element.VISIBILITY or Element.DISPLAY
6980          * @return {Roo.Element} this
6981          */
6982         setVisibilityMode : function(visMode){
6983             this.visibilityMode = visMode;
6984             return this;
6985         },
6986         /**
6987          * Convenience method for setVisibilityMode(Element.DISPLAY)
6988          * @param {String} display (optional) What to set display to when visible
6989          * @return {Roo.Element} this
6990          */
6991         enableDisplayMode : function(display){
6992             this.setVisibilityMode(El.DISPLAY);
6993             if(typeof display != "undefined") this.originalDisplay = display;
6994             return this;
6995         },
6996
6997         /**
6998          * 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)
6999          * @param {String} selector The simple selector to test
7000          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7001                 search as a number or element (defaults to 10 || document.body)
7002          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7003          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7004          */
7005         findParent : function(simpleSelector, maxDepth, returnEl){
7006             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7007             maxDepth = maxDepth || 50;
7008             if(typeof maxDepth != "number"){
7009                 stopEl = Roo.getDom(maxDepth);
7010                 maxDepth = 10;
7011             }
7012             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7013                 if(dq.is(p, simpleSelector)){
7014                     return returnEl ? Roo.get(p) : p;
7015                 }
7016                 depth++;
7017                 p = p.parentNode;
7018             }
7019             return null;
7020         },
7021
7022
7023         /**
7024          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7025          * @param {String} selector The simple selector to test
7026          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7027                 search as a number or element (defaults to 10 || document.body)
7028          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7029          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7030          */
7031         findParentNode : function(simpleSelector, maxDepth, returnEl){
7032             var p = Roo.fly(this.dom.parentNode, '_internal');
7033             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7034         },
7035
7036         /**
7037          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7038          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7039          * @param {String} selector The simple selector to test
7040          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7041                 search as a number or element (defaults to 10 || document.body)
7042          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7043          */
7044         up : function(simpleSelector, maxDepth){
7045             return this.findParentNode(simpleSelector, maxDepth, true);
7046         },
7047
7048
7049
7050         /**
7051          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7052          * @param {String} selector The simple selector to test
7053          * @return {Boolean} True if this element matches the selector, else false
7054          */
7055         is : function(simpleSelector){
7056             return Roo.DomQuery.is(this.dom, simpleSelector);
7057         },
7058
7059         /**
7060          * Perform animation on this element.
7061          * @param {Object} args The YUI animation control args
7062          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7063          * @param {Function} onComplete (optional) Function to call when animation completes
7064          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7065          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7066          * @return {Roo.Element} this
7067          */
7068         animate : function(args, duration, onComplete, easing, animType){
7069             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7070             return this;
7071         },
7072
7073         /*
7074          * @private Internal animation call
7075          */
7076         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7077             animType = animType || 'run';
7078             opt = opt || {};
7079             var anim = Roo.lib.Anim[animType](
7080                 this.dom, args,
7081                 (opt.duration || defaultDur) || .35,
7082                 (opt.easing || defaultEase) || 'easeOut',
7083                 function(){
7084                     Roo.callback(cb, this);
7085                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7086                 },
7087                 this
7088             );
7089             opt.anim = anim;
7090             return anim;
7091         },
7092
7093         // private legacy anim prep
7094         preanim : function(a, i){
7095             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7096         },
7097
7098         /**
7099          * Removes worthless text nodes
7100          * @param {Boolean} forceReclean (optional) By default the element
7101          * keeps track if it has been cleaned already so
7102          * you can call this over and over. However, if you update the element and
7103          * need to force a reclean, you can pass true.
7104          */
7105         clean : function(forceReclean){
7106             if(this.isCleaned && forceReclean !== true){
7107                 return this;
7108             }
7109             var ns = /\S/;
7110             var d = this.dom, n = d.firstChild, ni = -1;
7111             while(n){
7112                 var nx = n.nextSibling;
7113                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7114                     d.removeChild(n);
7115                 }else{
7116                     n.nodeIndex = ++ni;
7117                 }
7118                 n = nx;
7119             }
7120             this.isCleaned = true;
7121             return this;
7122         },
7123
7124         // private
7125         calcOffsetsTo : function(el){
7126             el = Roo.get(el);
7127             var d = el.dom;
7128             var restorePos = false;
7129             if(el.getStyle('position') == 'static'){
7130                 el.position('relative');
7131                 restorePos = true;
7132             }
7133             var x = 0, y =0;
7134             var op = this.dom;
7135             while(op && op != d && op.tagName != 'HTML'){
7136                 x+= op.offsetLeft;
7137                 y+= op.offsetTop;
7138                 op = op.offsetParent;
7139             }
7140             if(restorePos){
7141                 el.position('static');
7142             }
7143             return [x, y];
7144         },
7145
7146         /**
7147          * Scrolls this element into view within the passed container.
7148          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7149          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7150          * @return {Roo.Element} this
7151          */
7152         scrollIntoView : function(container, hscroll){
7153             var c = Roo.getDom(container) || document.body;
7154             var el = this.dom;
7155
7156             var o = this.calcOffsetsTo(c),
7157                 l = o[0],
7158                 t = o[1],
7159                 b = t+el.offsetHeight,
7160                 r = l+el.offsetWidth;
7161
7162             var ch = c.clientHeight;
7163             var ct = parseInt(c.scrollTop, 10);
7164             var cl = parseInt(c.scrollLeft, 10);
7165             var cb = ct + ch;
7166             var cr = cl + c.clientWidth;
7167
7168             if(t < ct){
7169                 c.scrollTop = t;
7170             }else if(b > cb){
7171                 c.scrollTop = b-ch;
7172             }
7173
7174             if(hscroll !== false){
7175                 if(l < cl){
7176                     c.scrollLeft = l;
7177                 }else if(r > cr){
7178                     c.scrollLeft = r-c.clientWidth;
7179                 }
7180             }
7181             return this;
7182         },
7183
7184         // private
7185         scrollChildIntoView : function(child, hscroll){
7186             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7187         },
7188
7189         /**
7190          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7191          * the new height may not be available immediately.
7192          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7193          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7194          * @param {Function} onComplete (optional) Function to call when animation completes
7195          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7196          * @return {Roo.Element} this
7197          */
7198         autoHeight : function(animate, duration, onComplete, easing){
7199             var oldHeight = this.getHeight();
7200             this.clip();
7201             this.setHeight(1); // force clipping
7202             setTimeout(function(){
7203                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7204                 if(!animate){
7205                     this.setHeight(height);
7206                     this.unclip();
7207                     if(typeof onComplete == "function"){
7208                         onComplete();
7209                     }
7210                 }else{
7211                     this.setHeight(oldHeight); // restore original height
7212                     this.setHeight(height, animate, duration, function(){
7213                         this.unclip();
7214                         if(typeof onComplete == "function") onComplete();
7215                     }.createDelegate(this), easing);
7216                 }
7217             }.createDelegate(this), 0);
7218             return this;
7219         },
7220
7221         /**
7222          * Returns true if this element is an ancestor of the passed element
7223          * @param {HTMLElement/String} el The element to check
7224          * @return {Boolean} True if this element is an ancestor of el, else false
7225          */
7226         contains : function(el){
7227             if(!el){return false;}
7228             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7229         },
7230
7231         /**
7232          * Checks whether the element is currently visible using both visibility and display properties.
7233          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7234          * @return {Boolean} True if the element is currently visible, else false
7235          */
7236         isVisible : function(deep) {
7237             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7238             if(deep !== true || !vis){
7239                 return vis;
7240             }
7241             var p = this.dom.parentNode;
7242             while(p && p.tagName.toLowerCase() != "body"){
7243                 if(!Roo.fly(p, '_isVisible').isVisible()){
7244                     return false;
7245                 }
7246                 p = p.parentNode;
7247             }
7248             return true;
7249         },
7250
7251         /**
7252          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7253          * @param {String} selector The CSS selector
7254          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7255          * @return {CompositeElement/CompositeElementLite} The composite element
7256          */
7257         select : function(selector, unique){
7258             return El.select(selector, unique, this.dom);
7259         },
7260
7261         /**
7262          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7263          * @param {String} selector The CSS selector
7264          * @return {Array} An array of the matched nodes
7265          */
7266         query : function(selector, unique){
7267             return Roo.DomQuery.select(selector, this.dom);
7268         },
7269
7270         /**
7271          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7272          * @param {String} selector The CSS selector
7273          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7274          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7275          */
7276         child : function(selector, returnDom){
7277             var n = Roo.DomQuery.selectNode(selector, this.dom);
7278             return returnDom ? n : Roo.get(n);
7279         },
7280
7281         /**
7282          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7283          * @param {String} selector The CSS selector
7284          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7285          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7286          */
7287         down : function(selector, returnDom){
7288             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7289             return returnDom ? n : Roo.get(n);
7290         },
7291
7292         /**
7293          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7294          * @param {String} group The group the DD object is member of
7295          * @param {Object} config The DD config object
7296          * @param {Object} overrides An object containing methods to override/implement on the DD object
7297          * @return {Roo.dd.DD} The DD object
7298          */
7299         initDD : function(group, config, overrides){
7300             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7301             return Roo.apply(dd, overrides);
7302         },
7303
7304         /**
7305          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7306          * @param {String} group The group the DDProxy object is member of
7307          * @param {Object} config The DDProxy config object
7308          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7309          * @return {Roo.dd.DDProxy} The DDProxy object
7310          */
7311         initDDProxy : function(group, config, overrides){
7312             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7313             return Roo.apply(dd, overrides);
7314         },
7315
7316         /**
7317          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7318          * @param {String} group The group the DDTarget object is member of
7319          * @param {Object} config The DDTarget config object
7320          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7321          * @return {Roo.dd.DDTarget} The DDTarget object
7322          */
7323         initDDTarget : function(group, config, overrides){
7324             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7325             return Roo.apply(dd, overrides);
7326         },
7327
7328         /**
7329          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7330          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7331          * @param {Boolean} visible Whether the element is visible
7332          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7333          * @return {Roo.Element} this
7334          */
7335          setVisible : function(visible, animate){
7336             if(!animate || !A){
7337                 if(this.visibilityMode == El.DISPLAY){
7338                     this.setDisplayed(visible);
7339                 }else{
7340                     this.fixDisplay();
7341                     this.dom.style.visibility = visible ? "visible" : "hidden";
7342                 }
7343             }else{
7344                 // closure for composites
7345                 var dom = this.dom;
7346                 var visMode = this.visibilityMode;
7347                 if(visible){
7348                     this.setOpacity(.01);
7349                     this.setVisible(true);
7350                 }
7351                 this.anim({opacity: { to: (visible?1:0) }},
7352                       this.preanim(arguments, 1),
7353                       null, .35, 'easeIn', function(){
7354                          if(!visible){
7355                              if(visMode == El.DISPLAY){
7356                                  dom.style.display = "none";
7357                              }else{
7358                                  dom.style.visibility = "hidden";
7359                              }
7360                              Roo.get(dom).setOpacity(1);
7361                          }
7362                      });
7363             }
7364             return this;
7365         },
7366
7367         /**
7368          * Returns true if display is not "none"
7369          * @return {Boolean}
7370          */
7371         isDisplayed : function() {
7372             return this.getStyle("display") != "none";
7373         },
7374
7375         /**
7376          * Toggles the element's visibility or display, depending on visibility mode.
7377          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7378          * @return {Roo.Element} this
7379          */
7380         toggle : function(animate){
7381             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7382             return this;
7383         },
7384
7385         /**
7386          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7387          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7388          * @return {Roo.Element} this
7389          */
7390         setDisplayed : function(value) {
7391             if(typeof value == "boolean"){
7392                value = value ? this.originalDisplay : "none";
7393             }
7394             this.setStyle("display", value);
7395             return this;
7396         },
7397
7398         /**
7399          * Tries to focus the element. Any exceptions are caught and ignored.
7400          * @return {Roo.Element} this
7401          */
7402         focus : function() {
7403             try{
7404                 this.dom.focus();
7405             }catch(e){}
7406             return this;
7407         },
7408
7409         /**
7410          * Tries to blur the element. Any exceptions are caught and ignored.
7411          * @return {Roo.Element} this
7412          */
7413         blur : function() {
7414             try{
7415                 this.dom.blur();
7416             }catch(e){}
7417             return this;
7418         },
7419
7420         /**
7421          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7422          * @param {String/Array} className The CSS class to add, or an array of classes
7423          * @return {Roo.Element} this
7424          */
7425         addClass : function(className){
7426             if(className instanceof Array){
7427                 for(var i = 0, len = className.length; i < len; i++) {
7428                     this.addClass(className[i]);
7429                 }
7430             }else{
7431                 if(className && !this.hasClass(className)){
7432                     this.dom.className = this.dom.className + " " + className;
7433                 }
7434             }
7435             return this;
7436         },
7437
7438         /**
7439          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7440          * @param {String/Array} className The CSS class to add, or an array of classes
7441          * @return {Roo.Element} this
7442          */
7443         radioClass : function(className){
7444             var siblings = this.dom.parentNode.childNodes;
7445             for(var i = 0; i < siblings.length; i++) {
7446                 var s = siblings[i];
7447                 if(s.nodeType == 1){
7448                     Roo.get(s).removeClass(className);
7449                 }
7450             }
7451             this.addClass(className);
7452             return this;
7453         },
7454
7455         /**
7456          * Removes one or more CSS classes from the element.
7457          * @param {String/Array} className The CSS class to remove, or an array of classes
7458          * @return {Roo.Element} this
7459          */
7460         removeClass : function(className){
7461             if(!className || !this.dom.className){
7462                 return this;
7463             }
7464             if(className instanceof Array){
7465                 for(var i = 0, len = className.length; i < len; i++) {
7466                     this.removeClass(className[i]);
7467                 }
7468             }else{
7469                 if(this.hasClass(className)){
7470                     var re = this.classReCache[className];
7471                     if (!re) {
7472                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7473                        this.classReCache[className] = re;
7474                     }
7475                     this.dom.className =
7476                         this.dom.className.replace(re, " ");
7477                 }
7478             }
7479             return this;
7480         },
7481
7482         // private
7483         classReCache: {},
7484
7485         /**
7486          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7487          * @param {String} className The CSS class to toggle
7488          * @return {Roo.Element} this
7489          */
7490         toggleClass : function(className){
7491             if(this.hasClass(className)){
7492                 this.removeClass(className);
7493             }else{
7494                 this.addClass(className);
7495             }
7496             return this;
7497         },
7498
7499         /**
7500          * Checks if the specified CSS class exists on this element's DOM node.
7501          * @param {String} className The CSS class to check for
7502          * @return {Boolean} True if the class exists, else false
7503          */
7504         hasClass : function(className){
7505             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7506         },
7507
7508         /**
7509          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7510          * @param {String} oldClassName The CSS class to replace
7511          * @param {String} newClassName The replacement CSS class
7512          * @return {Roo.Element} this
7513          */
7514         replaceClass : function(oldClassName, newClassName){
7515             this.removeClass(oldClassName);
7516             this.addClass(newClassName);
7517             return this;
7518         },
7519
7520         /**
7521          * Returns an object with properties matching the styles requested.
7522          * For example, el.getStyles('color', 'font-size', 'width') might return
7523          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7524          * @param {String} style1 A style name
7525          * @param {String} style2 A style name
7526          * @param {String} etc.
7527          * @return {Object} The style object
7528          */
7529         getStyles : function(){
7530             var a = arguments, len = a.length, r = {};
7531             for(var i = 0; i < len; i++){
7532                 r[a[i]] = this.getStyle(a[i]);
7533             }
7534             return r;
7535         },
7536
7537         /**
7538          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7539          * @param {String} property The style property whose value is returned.
7540          * @return {String} The current value of the style property for this element.
7541          */
7542         getStyle : function(){
7543             return view && view.getComputedStyle ?
7544                 function(prop){
7545                     var el = this.dom, v, cs, camel;
7546                     if(prop == 'float'){
7547                         prop = "cssFloat";
7548                     }
7549                     if(el.style && (v = el.style[prop])){
7550                         return v;
7551                     }
7552                     if(cs = view.getComputedStyle(el, "")){
7553                         if(!(camel = propCache[prop])){
7554                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7555                         }
7556                         return cs[camel];
7557                     }
7558                     return null;
7559                 } :
7560                 function(prop){
7561                     var el = this.dom, v, cs, camel;
7562                     if(prop == 'opacity'){
7563                         if(typeof el.style.filter == 'string'){
7564                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7565                             if(m){
7566                                 var fv = parseFloat(m[1]);
7567                                 if(!isNaN(fv)){
7568                                     return fv ? fv / 100 : 0;
7569                                 }
7570                             }
7571                         }
7572                         return 1;
7573                     }else if(prop == 'float'){
7574                         prop = "styleFloat";
7575                     }
7576                     if(!(camel = propCache[prop])){
7577                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7578                     }
7579                     if(v = el.style[camel]){
7580                         return v;
7581                     }
7582                     if(cs = el.currentStyle){
7583                         return cs[camel];
7584                     }
7585                     return null;
7586                 };
7587         }(),
7588
7589         /**
7590          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7591          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7592          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7593          * @return {Roo.Element} this
7594          */
7595         setStyle : function(prop, value){
7596             if(typeof prop == "string"){
7597                 
7598                 if (prop == 'float') {
7599                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7600                     return this;
7601                 }
7602                 
7603                 var camel;
7604                 if(!(camel = propCache[prop])){
7605                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7606                 }
7607                 
7608                 if(camel == 'opacity') {
7609                     this.setOpacity(value);
7610                 }else{
7611                     this.dom.style[camel] = value;
7612                 }
7613             }else{
7614                 for(var style in prop){
7615                     if(typeof prop[style] != "function"){
7616                        this.setStyle(style, prop[style]);
7617                     }
7618                 }
7619             }
7620             return this;
7621         },
7622
7623         /**
7624          * More flexible version of {@link #setStyle} for setting style properties.
7625          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7626          * a function which returns such a specification.
7627          * @return {Roo.Element} this
7628          */
7629         applyStyles : function(style){
7630             Roo.DomHelper.applyStyles(this.dom, style);
7631             return this;
7632         },
7633
7634         /**
7635           * 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).
7636           * @return {Number} The X position of the element
7637           */
7638         getX : function(){
7639             return D.getX(this.dom);
7640         },
7641
7642         /**
7643           * 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).
7644           * @return {Number} The Y position of the element
7645           */
7646         getY : function(){
7647             return D.getY(this.dom);
7648         },
7649
7650         /**
7651           * 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).
7652           * @return {Array} The XY position of the element
7653           */
7654         getXY : function(){
7655             return D.getXY(this.dom);
7656         },
7657
7658         /**
7659          * 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).
7660          * @param {Number} The X position of the element
7661          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7662          * @return {Roo.Element} this
7663          */
7664         setX : function(x, animate){
7665             if(!animate || !A){
7666                 D.setX(this.dom, x);
7667             }else{
7668                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7669             }
7670             return this;
7671         },
7672
7673         /**
7674          * 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).
7675          * @param {Number} The Y position of the element
7676          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7677          * @return {Roo.Element} this
7678          */
7679         setY : function(y, animate){
7680             if(!animate || !A){
7681                 D.setY(this.dom, y);
7682             }else{
7683                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7684             }
7685             return this;
7686         },
7687
7688         /**
7689          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7690          * @param {String} left The left CSS property value
7691          * @return {Roo.Element} this
7692          */
7693         setLeft : function(left){
7694             this.setStyle("left", this.addUnits(left));
7695             return this;
7696         },
7697
7698         /**
7699          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7700          * @param {String} top The top CSS property value
7701          * @return {Roo.Element} this
7702          */
7703         setTop : function(top){
7704             this.setStyle("top", this.addUnits(top));
7705             return this;
7706         },
7707
7708         /**
7709          * Sets the element's CSS right style.
7710          * @param {String} right The right CSS property value
7711          * @return {Roo.Element} this
7712          */
7713         setRight : function(right){
7714             this.setStyle("right", this.addUnits(right));
7715             return this;
7716         },
7717
7718         /**
7719          * Sets the element's CSS bottom style.
7720          * @param {String} bottom The bottom CSS property value
7721          * @return {Roo.Element} this
7722          */
7723         setBottom : function(bottom){
7724             this.setStyle("bottom", this.addUnits(bottom));
7725             return this;
7726         },
7727
7728         /**
7729          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7730          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7731          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7732          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7733          * @return {Roo.Element} this
7734          */
7735         setXY : function(pos, animate){
7736             if(!animate || !A){
7737                 D.setXY(this.dom, pos);
7738             }else{
7739                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7740             }
7741             return this;
7742         },
7743
7744         /**
7745          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7746          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7747          * @param {Number} x X value for new position (coordinates are page-based)
7748          * @param {Number} y Y value for new position (coordinates are page-based)
7749          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7750          * @return {Roo.Element} this
7751          */
7752         setLocation : function(x, y, animate){
7753             this.setXY([x, y], this.preanim(arguments, 2));
7754             return this;
7755         },
7756
7757         /**
7758          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7759          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7760          * @param {Number} x X value for new position (coordinates are page-based)
7761          * @param {Number} y Y value for new position (coordinates are page-based)
7762          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7763          * @return {Roo.Element} this
7764          */
7765         moveTo : function(x, y, animate){
7766             this.setXY([x, y], this.preanim(arguments, 2));
7767             return this;
7768         },
7769
7770         /**
7771          * Returns the region of the given element.
7772          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7773          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7774          */
7775         getRegion : function(){
7776             return D.getRegion(this.dom);
7777         },
7778
7779         /**
7780          * Returns the offset height of the element
7781          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7782          * @return {Number} The element's height
7783          */
7784         getHeight : function(contentHeight){
7785             var h = this.dom.offsetHeight || 0;
7786             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7787         },
7788
7789         /**
7790          * Returns the offset width of the element
7791          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7792          * @return {Number} The element's width
7793          */
7794         getWidth : function(contentWidth){
7795             var w = this.dom.offsetWidth || 0;
7796             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7797         },
7798
7799         /**
7800          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7801          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7802          * if a height has not been set using CSS.
7803          * @return {Number}
7804          */
7805         getComputedHeight : function(){
7806             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7807             if(!h){
7808                 h = parseInt(this.getStyle('height'), 10) || 0;
7809                 if(!this.isBorderBox()){
7810                     h += this.getFrameWidth('tb');
7811                 }
7812             }
7813             return h;
7814         },
7815
7816         /**
7817          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7818          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7819          * if a width has not been set using CSS.
7820          * @return {Number}
7821          */
7822         getComputedWidth : function(){
7823             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7824             if(!w){
7825                 w = parseInt(this.getStyle('width'), 10) || 0;
7826                 if(!this.isBorderBox()){
7827                     w += this.getFrameWidth('lr');
7828                 }
7829             }
7830             return w;
7831         },
7832
7833         /**
7834          * Returns the size of the element.
7835          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7836          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7837          */
7838         getSize : function(contentSize){
7839             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7840         },
7841
7842         /**
7843          * Returns the width and height of the viewport.
7844          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7845          */
7846         getViewSize : function(){
7847             var d = this.dom, doc = document, aw = 0, ah = 0;
7848             if(d == doc || d == doc.body){
7849                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7850             }else{
7851                 return {
7852                     width : d.clientWidth,
7853                     height: d.clientHeight
7854                 };
7855             }
7856         },
7857
7858         /**
7859          * Returns the value of the "value" attribute
7860          * @param {Boolean} asNumber true to parse the value as a number
7861          * @return {String/Number}
7862          */
7863         getValue : function(asNumber){
7864             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7865         },
7866
7867         // private
7868         adjustWidth : function(width){
7869             if(typeof width == "number"){
7870                 if(this.autoBoxAdjust && !this.isBorderBox()){
7871                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7872                 }
7873                 if(width < 0){
7874                     width = 0;
7875                 }
7876             }
7877             return width;
7878         },
7879
7880         // private
7881         adjustHeight : function(height){
7882             if(typeof height == "number"){
7883                if(this.autoBoxAdjust && !this.isBorderBox()){
7884                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7885                }
7886                if(height < 0){
7887                    height = 0;
7888                }
7889             }
7890             return height;
7891         },
7892
7893         /**
7894          * Set the width of the element
7895          * @param {Number} width The new width
7896          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7897          * @return {Roo.Element} this
7898          */
7899         setWidth : function(width, animate){
7900             width = this.adjustWidth(width);
7901             if(!animate || !A){
7902                 this.dom.style.width = this.addUnits(width);
7903             }else{
7904                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7905             }
7906             return this;
7907         },
7908
7909         /**
7910          * Set the height of the element
7911          * @param {Number} height The new height
7912          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7913          * @return {Roo.Element} this
7914          */
7915          setHeight : function(height, animate){
7916             height = this.adjustHeight(height);
7917             if(!animate || !A){
7918                 this.dom.style.height = this.addUnits(height);
7919             }else{
7920                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7921             }
7922             return this;
7923         },
7924
7925         /**
7926          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7927          * @param {Number} width The new width
7928          * @param {Number} height The new height
7929          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7930          * @return {Roo.Element} this
7931          */
7932          setSize : function(width, height, animate){
7933             if(typeof width == "object"){ // in case of object from getSize()
7934                 height = width.height; width = width.width;
7935             }
7936             width = this.adjustWidth(width); height = this.adjustHeight(height);
7937             if(!animate || !A){
7938                 this.dom.style.width = this.addUnits(width);
7939                 this.dom.style.height = this.addUnits(height);
7940             }else{
7941                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7942             }
7943             return this;
7944         },
7945
7946         /**
7947          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7948          * @param {Number} x X value for new position (coordinates are page-based)
7949          * @param {Number} y Y value for new position (coordinates are page-based)
7950          * @param {Number} width The new width
7951          * @param {Number} height The new height
7952          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7953          * @return {Roo.Element} this
7954          */
7955         setBounds : function(x, y, width, height, animate){
7956             if(!animate || !A){
7957                 this.setSize(width, height);
7958                 this.setLocation(x, y);
7959             }else{
7960                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7961                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7962                               this.preanim(arguments, 4), 'motion');
7963             }
7964             return this;
7965         },
7966
7967         /**
7968          * 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.
7969          * @param {Roo.lib.Region} region The region to fill
7970          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7971          * @return {Roo.Element} this
7972          */
7973         setRegion : function(region, animate){
7974             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7975             return this;
7976         },
7977
7978         /**
7979          * Appends an event handler
7980          *
7981          * @param {String}   eventName     The type of event to append
7982          * @param {Function} fn        The method the event invokes
7983          * @param {Object} scope       (optional) The scope (this object) of the fn
7984          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7985          */
7986         addListener : function(eventName, fn, scope, options){
7987             if (this.dom) {
7988                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7989             }
7990         },
7991
7992         /**
7993          * Removes an event handler from this element
7994          * @param {String} eventName the type of event to remove
7995          * @param {Function} fn the method the event invokes
7996          * @return {Roo.Element} this
7997          */
7998         removeListener : function(eventName, fn){
7999             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8000             return this;
8001         },
8002
8003         /**
8004          * Removes all previous added listeners from this element
8005          * @return {Roo.Element} this
8006          */
8007         removeAllListeners : function(){
8008             E.purgeElement(this.dom);
8009             return this;
8010         },
8011
8012         relayEvent : function(eventName, observable){
8013             this.on(eventName, function(e){
8014                 observable.fireEvent(eventName, e);
8015             });
8016         },
8017
8018         /**
8019          * Set the opacity of the element
8020          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8021          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8022          * @return {Roo.Element} this
8023          */
8024          setOpacity : function(opacity, animate){
8025             if(!animate || !A){
8026                 var s = this.dom.style;
8027                 if(Roo.isIE){
8028                     s.zoom = 1;
8029                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8030                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8031                 }else{
8032                     s.opacity = opacity;
8033                 }
8034             }else{
8035                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8036             }
8037             return this;
8038         },
8039
8040         /**
8041          * Gets the left X coordinate
8042          * @param {Boolean} local True to get the local css position instead of page coordinate
8043          * @return {Number}
8044          */
8045         getLeft : function(local){
8046             if(!local){
8047                 return this.getX();
8048             }else{
8049                 return parseInt(this.getStyle("left"), 10) || 0;
8050             }
8051         },
8052
8053         /**
8054          * Gets the right X coordinate of the element (element X position + element width)
8055          * @param {Boolean} local True to get the local css position instead of page coordinate
8056          * @return {Number}
8057          */
8058         getRight : function(local){
8059             if(!local){
8060                 return this.getX() + this.getWidth();
8061             }else{
8062                 return (this.getLeft(true) + this.getWidth()) || 0;
8063             }
8064         },
8065
8066         /**
8067          * Gets the top Y coordinate
8068          * @param {Boolean} local True to get the local css position instead of page coordinate
8069          * @return {Number}
8070          */
8071         getTop : function(local) {
8072             if(!local){
8073                 return this.getY();
8074             }else{
8075                 return parseInt(this.getStyle("top"), 10) || 0;
8076             }
8077         },
8078
8079         /**
8080          * Gets the bottom Y coordinate of the element (element Y position + element height)
8081          * @param {Boolean} local True to get the local css position instead of page coordinate
8082          * @return {Number}
8083          */
8084         getBottom : function(local){
8085             if(!local){
8086                 return this.getY() + this.getHeight();
8087             }else{
8088                 return (this.getTop(true) + this.getHeight()) || 0;
8089             }
8090         },
8091
8092         /**
8093         * Initializes positioning on this element. If a desired position is not passed, it will make the
8094         * the element positioned relative IF it is not already positioned.
8095         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8096         * @param {Number} zIndex (optional) The zIndex to apply
8097         * @param {Number} x (optional) Set the page X position
8098         * @param {Number} y (optional) Set the page Y position
8099         */
8100         position : function(pos, zIndex, x, y){
8101             if(!pos){
8102                if(this.getStyle('position') == 'static'){
8103                    this.setStyle('position', 'relative');
8104                }
8105             }else{
8106                 this.setStyle("position", pos);
8107             }
8108             if(zIndex){
8109                 this.setStyle("z-index", zIndex);
8110             }
8111             if(x !== undefined && y !== undefined){
8112                 this.setXY([x, y]);
8113             }else if(x !== undefined){
8114                 this.setX(x);
8115             }else if(y !== undefined){
8116                 this.setY(y);
8117             }
8118         },
8119
8120         /**
8121         * Clear positioning back to the default when the document was loaded
8122         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8123         * @return {Roo.Element} this
8124          */
8125         clearPositioning : function(value){
8126             value = value ||'';
8127             this.setStyle({
8128                 "left": value,
8129                 "right": value,
8130                 "top": value,
8131                 "bottom": value,
8132                 "z-index": "",
8133                 "position" : "static"
8134             });
8135             return this;
8136         },
8137
8138         /**
8139         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8140         * snapshot before performing an update and then restoring the element.
8141         * @return {Object}
8142         */
8143         getPositioning : function(){
8144             var l = this.getStyle("left");
8145             var t = this.getStyle("top");
8146             return {
8147                 "position" : this.getStyle("position"),
8148                 "left" : l,
8149                 "right" : l ? "" : this.getStyle("right"),
8150                 "top" : t,
8151                 "bottom" : t ? "" : this.getStyle("bottom"),
8152                 "z-index" : this.getStyle("z-index")
8153             };
8154         },
8155
8156         /**
8157          * Gets the width of the border(s) for the specified side(s)
8158          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8159          * passing lr would get the border (l)eft width + the border (r)ight width.
8160          * @return {Number} The width of the sides passed added together
8161          */
8162         getBorderWidth : function(side){
8163             return this.addStyles(side, El.borders);
8164         },
8165
8166         /**
8167          * Gets the width of the padding(s) for the specified side(s)
8168          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8169          * passing lr would get the padding (l)eft + the padding (r)ight.
8170          * @return {Number} The padding of the sides passed added together
8171          */
8172         getPadding : function(side){
8173             return this.addStyles(side, El.paddings);
8174         },
8175
8176         /**
8177         * Set positioning with an object returned by getPositioning().
8178         * @param {Object} posCfg
8179         * @return {Roo.Element} this
8180          */
8181         setPositioning : function(pc){
8182             this.applyStyles(pc);
8183             if(pc.right == "auto"){
8184                 this.dom.style.right = "";
8185             }
8186             if(pc.bottom == "auto"){
8187                 this.dom.style.bottom = "";
8188             }
8189             return this;
8190         },
8191
8192         // private
8193         fixDisplay : function(){
8194             if(this.getStyle("display") == "none"){
8195                 this.setStyle("visibility", "hidden");
8196                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8197                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8198                     this.setStyle("display", "block");
8199                 }
8200             }
8201         },
8202
8203         /**
8204          * Quick set left and top adding default units
8205          * @param {String} left The left CSS property value
8206          * @param {String} top The top CSS property value
8207          * @return {Roo.Element} this
8208          */
8209          setLeftTop : function(left, top){
8210             this.dom.style.left = this.addUnits(left);
8211             this.dom.style.top = this.addUnits(top);
8212             return this;
8213         },
8214
8215         /**
8216          * Move this element relative to its current position.
8217          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8218          * @param {Number} distance How far to move the element in pixels
8219          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8220          * @return {Roo.Element} this
8221          */
8222          move : function(direction, distance, animate){
8223             var xy = this.getXY();
8224             direction = direction.toLowerCase();
8225             switch(direction){
8226                 case "l":
8227                 case "left":
8228                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8229                     break;
8230                case "r":
8231                case "right":
8232                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8233                     break;
8234                case "t":
8235                case "top":
8236                case "up":
8237                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8238                     break;
8239                case "b":
8240                case "bottom":
8241                case "down":
8242                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8243                     break;
8244             }
8245             return this;
8246         },
8247
8248         /**
8249          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8250          * @return {Roo.Element} this
8251          */
8252         clip : function(){
8253             if(!this.isClipped){
8254                this.isClipped = true;
8255                this.originalClip = {
8256                    "o": this.getStyle("overflow"),
8257                    "x": this.getStyle("overflow-x"),
8258                    "y": this.getStyle("overflow-y")
8259                };
8260                this.setStyle("overflow", "hidden");
8261                this.setStyle("overflow-x", "hidden");
8262                this.setStyle("overflow-y", "hidden");
8263             }
8264             return this;
8265         },
8266
8267         /**
8268          *  Return clipping (overflow) to original clipping before clip() was called
8269          * @return {Roo.Element} this
8270          */
8271         unclip : function(){
8272             if(this.isClipped){
8273                 this.isClipped = false;
8274                 var o = this.originalClip;
8275                 if(o.o){this.setStyle("overflow", o.o);}
8276                 if(o.x){this.setStyle("overflow-x", o.x);}
8277                 if(o.y){this.setStyle("overflow-y", o.y);}
8278             }
8279             return this;
8280         },
8281
8282
8283         /**
8284          * Gets the x,y coordinates specified by the anchor position on the element.
8285          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8286          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8287          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8288          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8289          * @return {Array} [x, y] An array containing the element's x and y coordinates
8290          */
8291         getAnchorXY : function(anchor, local, s){
8292             //Passing a different size is useful for pre-calculating anchors,
8293             //especially for anchored animations that change the el size.
8294
8295             var w, h, vp = false;
8296             if(!s){
8297                 var d = this.dom;
8298                 if(d == document.body || d == document){
8299                     vp = true;
8300                     w = D.getViewWidth(); h = D.getViewHeight();
8301                 }else{
8302                     w = this.getWidth(); h = this.getHeight();
8303                 }
8304             }else{
8305                 w = s.width;  h = s.height;
8306             }
8307             var x = 0, y = 0, r = Math.round;
8308             switch((anchor || "tl").toLowerCase()){
8309                 case "c":
8310                     x = r(w*.5);
8311                     y = r(h*.5);
8312                 break;
8313                 case "t":
8314                     x = r(w*.5);
8315                     y = 0;
8316                 break;
8317                 case "l":
8318                     x = 0;
8319                     y = r(h*.5);
8320                 break;
8321                 case "r":
8322                     x = w;
8323                     y = r(h*.5);
8324                 break;
8325                 case "b":
8326                     x = r(w*.5);
8327                     y = h;
8328                 break;
8329                 case "tl":
8330                     x = 0;
8331                     y = 0;
8332                 break;
8333                 case "bl":
8334                     x = 0;
8335                     y = h;
8336                 break;
8337                 case "br":
8338                     x = w;
8339                     y = h;
8340                 break;
8341                 case "tr":
8342                     x = w;
8343                     y = 0;
8344                 break;
8345             }
8346             if(local === true){
8347                 return [x, y];
8348             }
8349             if(vp){
8350                 var sc = this.getScroll();
8351                 return [x + sc.left, y + sc.top];
8352             }
8353             //Add the element's offset xy
8354             var o = this.getXY();
8355             return [x+o[0], y+o[1]];
8356         },
8357
8358         /**
8359          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8360          * supported position values.
8361          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8362          * @param {String} position The position to align to.
8363          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8364          * @return {Array} [x, y]
8365          */
8366         getAlignToXY : function(el, p, o){
8367             el = Roo.get(el);
8368             var d = this.dom;
8369             if(!el.dom){
8370                 throw "Element.alignTo with an element that doesn't exist";
8371             }
8372             var c = false; //constrain to viewport
8373             var p1 = "", p2 = "";
8374             o = o || [0,0];
8375
8376             if(!p){
8377                 p = "tl-bl";
8378             }else if(p == "?"){
8379                 p = "tl-bl?";
8380             }else if(p.indexOf("-") == -1){
8381                 p = "tl-" + p;
8382             }
8383             p = p.toLowerCase();
8384             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8385             if(!m){
8386                throw "Element.alignTo with an invalid alignment " + p;
8387             }
8388             p1 = m[1]; p2 = m[2]; c = !!m[3];
8389
8390             //Subtract the aligned el's internal xy from the target's offset xy
8391             //plus custom offset to get the aligned el's new offset xy
8392             var a1 = this.getAnchorXY(p1, true);
8393             var a2 = el.getAnchorXY(p2, false);
8394             var x = a2[0] - a1[0] + o[0];
8395             var y = a2[1] - a1[1] + o[1];
8396             if(c){
8397                 //constrain the aligned el to viewport if necessary
8398                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8399                 // 5px of margin for ie
8400                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8401
8402                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8403                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8404                 //otherwise swap the aligned el to the opposite border of the target.
8405                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8406                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8407                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8408                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8409
8410                var doc = document;
8411                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8412                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8413
8414                if((x+w) > dw + scrollX){
8415                     x = swapX ? r.left-w : dw+scrollX-w;
8416                 }
8417                if(x < scrollX){
8418                    x = swapX ? r.right : scrollX;
8419                }
8420                if((y+h) > dh + scrollY){
8421                     y = swapY ? r.top-h : dh+scrollY-h;
8422                 }
8423                if (y < scrollY){
8424                    y = swapY ? r.bottom : scrollY;
8425                }
8426             }
8427             return [x,y];
8428         },
8429
8430         // private
8431         getConstrainToXY : function(){
8432             var os = {top:0, left:0, bottom:0, right: 0};
8433
8434             return function(el, local, offsets, proposedXY){
8435                 el = Roo.get(el);
8436                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8437
8438                 var vw, vh, vx = 0, vy = 0;
8439                 if(el.dom == document.body || el.dom == document){
8440                     vw = Roo.lib.Dom.getViewWidth();
8441                     vh = Roo.lib.Dom.getViewHeight();
8442                 }else{
8443                     vw = el.dom.clientWidth;
8444                     vh = el.dom.clientHeight;
8445                     if(!local){
8446                         var vxy = el.getXY();
8447                         vx = vxy[0];
8448                         vy = vxy[1];
8449                     }
8450                 }
8451
8452                 var s = el.getScroll();
8453
8454                 vx += offsets.left + s.left;
8455                 vy += offsets.top + s.top;
8456
8457                 vw -= offsets.right;
8458                 vh -= offsets.bottom;
8459
8460                 var vr = vx+vw;
8461                 var vb = vy+vh;
8462
8463                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8464                 var x = xy[0], y = xy[1];
8465                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8466
8467                 // only move it if it needs it
8468                 var moved = false;
8469
8470                 // first validate right/bottom
8471                 if((x + w) > vr){
8472                     x = vr - w;
8473                     moved = true;
8474                 }
8475                 if((y + h) > vb){
8476                     y = vb - h;
8477                     moved = true;
8478                 }
8479                 // then make sure top/left isn't negative
8480                 if(x < vx){
8481                     x = vx;
8482                     moved = true;
8483                 }
8484                 if(y < vy){
8485                     y = vy;
8486                     moved = true;
8487                 }
8488                 return moved ? [x, y] : false;
8489             };
8490         }(),
8491
8492         // private
8493         adjustForConstraints : function(xy, parent, offsets){
8494             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8495         },
8496
8497         /**
8498          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8499          * document it aligns it to the viewport.
8500          * The position parameter is optional, and can be specified in any one of the following formats:
8501          * <ul>
8502          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8503          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8504          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8505          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8506          *   <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
8507          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8508          * </ul>
8509          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8510          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8511          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8512          * that specified in order to enforce the viewport constraints.
8513          * Following are all of the supported anchor positions:
8514     <pre>
8515     Value  Description
8516     -----  -----------------------------
8517     tl     The top left corner (default)
8518     t      The center of the top edge
8519     tr     The top right corner
8520     l      The center of the left edge
8521     c      In the center of the element
8522     r      The center of the right edge
8523     bl     The bottom left corner
8524     b      The center of the bottom edge
8525     br     The bottom right corner
8526     </pre>
8527     Example Usage:
8528     <pre><code>
8529     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8530     el.alignTo("other-el");
8531
8532     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8533     el.alignTo("other-el", "tr?");
8534
8535     // align the bottom right corner of el with the center left edge of other-el
8536     el.alignTo("other-el", "br-l?");
8537
8538     // align the center of el with the bottom left corner of other-el and
8539     // adjust the x position by -6 pixels (and the y position by 0)
8540     el.alignTo("other-el", "c-bl", [-6, 0]);
8541     </code></pre>
8542          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8543          * @param {String} position The position to align to.
8544          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8545          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8546          * @return {Roo.Element} this
8547          */
8548         alignTo : function(element, position, offsets, animate){
8549             var xy = this.getAlignToXY(element, position, offsets);
8550             this.setXY(xy, this.preanim(arguments, 3));
8551             return this;
8552         },
8553
8554         /**
8555          * Anchors an element to another element and realigns it when the window is resized.
8556          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8557          * @param {String} position The position to align to.
8558          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8559          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8560          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8561          * is a number, it is used as the buffer delay (defaults to 50ms).
8562          * @param {Function} callback The function to call after the animation finishes
8563          * @return {Roo.Element} this
8564          */
8565         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8566             var action = function(){
8567                 this.alignTo(el, alignment, offsets, animate);
8568                 Roo.callback(callback, this);
8569             };
8570             Roo.EventManager.onWindowResize(action, this);
8571             var tm = typeof monitorScroll;
8572             if(tm != 'undefined'){
8573                 Roo.EventManager.on(window, 'scroll', action, this,
8574                     {buffer: tm == 'number' ? monitorScroll : 50});
8575             }
8576             action.call(this); // align immediately
8577             return this;
8578         },
8579         /**
8580          * Clears any opacity settings from this element. Required in some cases for IE.
8581          * @return {Roo.Element} this
8582          */
8583         clearOpacity : function(){
8584             if (window.ActiveXObject) {
8585                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8586                     this.dom.style.filter = "";
8587                 }
8588             } else {
8589                 this.dom.style.opacity = "";
8590                 this.dom.style["-moz-opacity"] = "";
8591                 this.dom.style["-khtml-opacity"] = "";
8592             }
8593             return this;
8594         },
8595
8596         /**
8597          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8598          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8599          * @return {Roo.Element} this
8600          */
8601         hide : function(animate){
8602             this.setVisible(false, this.preanim(arguments, 0));
8603             return this;
8604         },
8605
8606         /**
8607         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8608         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8609          * @return {Roo.Element} this
8610          */
8611         show : function(animate){
8612             this.setVisible(true, this.preanim(arguments, 0));
8613             return this;
8614         },
8615
8616         /**
8617          * @private Test if size has a unit, otherwise appends the default
8618          */
8619         addUnits : function(size){
8620             return Roo.Element.addUnits(size, this.defaultUnit);
8621         },
8622
8623         /**
8624          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8625          * @return {Roo.Element} this
8626          */
8627         beginMeasure : function(){
8628             var el = this.dom;
8629             if(el.offsetWidth || el.offsetHeight){
8630                 return this; // offsets work already
8631             }
8632             var changed = [];
8633             var p = this.dom, b = document.body; // start with this element
8634             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8635                 var pe = Roo.get(p);
8636                 if(pe.getStyle('display') == 'none'){
8637                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8638                     p.style.visibility = "hidden";
8639                     p.style.display = "block";
8640                 }
8641                 p = p.parentNode;
8642             }
8643             this._measureChanged = changed;
8644             return this;
8645
8646         },
8647
8648         /**
8649          * Restores displays to before beginMeasure was called
8650          * @return {Roo.Element} this
8651          */
8652         endMeasure : function(){
8653             var changed = this._measureChanged;
8654             if(changed){
8655                 for(var i = 0, len = changed.length; i < len; i++) {
8656                     var r = changed[i];
8657                     r.el.style.visibility = r.visibility;
8658                     r.el.style.display = "none";
8659                 }
8660                 this._measureChanged = null;
8661             }
8662             return this;
8663         },
8664
8665         /**
8666         * Update the innerHTML of this element, optionally searching for and processing scripts
8667         * @param {String} html The new HTML
8668         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8669         * @param {Function} callback For async script loading you can be noticed when the update completes
8670         * @return {Roo.Element} this
8671          */
8672         update : function(html, loadScripts, callback){
8673             if(typeof html == "undefined"){
8674                 html = "";
8675             }
8676             if(loadScripts !== true){
8677                 this.dom.innerHTML = html;
8678                 if(typeof callback == "function"){
8679                     callback();
8680                 }
8681                 return this;
8682             }
8683             var id = Roo.id();
8684             var dom = this.dom;
8685
8686             html += '<span id="' + id + '"></span>';
8687
8688             E.onAvailable(id, function(){
8689                 var hd = document.getElementsByTagName("head")[0];
8690                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8691                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8692                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8693
8694                 var match;
8695                 while(match = re.exec(html)){
8696                     var attrs = match[1];
8697                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8698                     if(srcMatch && srcMatch[2]){
8699                        var s = document.createElement("script");
8700                        s.src = srcMatch[2];
8701                        var typeMatch = attrs.match(typeRe);
8702                        if(typeMatch && typeMatch[2]){
8703                            s.type = typeMatch[2];
8704                        }
8705                        hd.appendChild(s);
8706                     }else if(match[2] && match[2].length > 0){
8707                         if(window.execScript) {
8708                            window.execScript(match[2]);
8709                         } else {
8710                             /**
8711                              * eval:var:id
8712                              * eval:var:dom
8713                              * eval:var:html
8714                              * 
8715                              */
8716                            window.eval(match[2]);
8717                         }
8718                     }
8719                 }
8720                 var el = document.getElementById(id);
8721                 if(el){el.parentNode.removeChild(el);}
8722                 if(typeof callback == "function"){
8723                     callback();
8724                 }
8725             });
8726             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8727             return this;
8728         },
8729
8730         /**
8731          * Direct access to the UpdateManager update() method (takes the same parameters).
8732          * @param {String/Function} url The url for this request or a function to call to get the url
8733          * @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}
8734          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8735          * @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.
8736          * @return {Roo.Element} this
8737          */
8738         load : function(){
8739             var um = this.getUpdateManager();
8740             um.update.apply(um, arguments);
8741             return this;
8742         },
8743
8744         /**
8745         * Gets this element's UpdateManager
8746         * @return {Roo.UpdateManager} The UpdateManager
8747         */
8748         getUpdateManager : function(){
8749             if(!this.updateManager){
8750                 this.updateManager = new Roo.UpdateManager(this);
8751             }
8752             return this.updateManager;
8753         },
8754
8755         /**
8756          * Disables text selection for this element (normalized across browsers)
8757          * @return {Roo.Element} this
8758          */
8759         unselectable : function(){
8760             this.dom.unselectable = "on";
8761             this.swallowEvent("selectstart", true);
8762             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8763             this.addClass("x-unselectable");
8764             return this;
8765         },
8766
8767         /**
8768         * Calculates the x, y to center this element on the screen
8769         * @return {Array} The x, y values [x, y]
8770         */
8771         getCenterXY : function(){
8772             return this.getAlignToXY(document, 'c-c');
8773         },
8774
8775         /**
8776         * Centers the Element in either the viewport, or another Element.
8777         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8778         */
8779         center : function(centerIn){
8780             this.alignTo(centerIn || document, 'c-c');
8781             return this;
8782         },
8783
8784         /**
8785          * Tests various css rules/browsers to determine if this element uses a border box
8786          * @return {Boolean}
8787          */
8788         isBorderBox : function(){
8789             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8790         },
8791
8792         /**
8793          * Return a box {x, y, width, height} that can be used to set another elements
8794          * size/location to match this element.
8795          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8796          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8797          * @return {Object} box An object in the format {x, y, width, height}
8798          */
8799         getBox : function(contentBox, local){
8800             var xy;
8801             if(!local){
8802                 xy = this.getXY();
8803             }else{
8804                 var left = parseInt(this.getStyle("left"), 10) || 0;
8805                 var top = parseInt(this.getStyle("top"), 10) || 0;
8806                 xy = [left, top];
8807             }
8808             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8809             if(!contentBox){
8810                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8811             }else{
8812                 var l = this.getBorderWidth("l")+this.getPadding("l");
8813                 var r = this.getBorderWidth("r")+this.getPadding("r");
8814                 var t = this.getBorderWidth("t")+this.getPadding("t");
8815                 var b = this.getBorderWidth("b")+this.getPadding("b");
8816                 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)};
8817             }
8818             bx.right = bx.x + bx.width;
8819             bx.bottom = bx.y + bx.height;
8820             return bx;
8821         },
8822
8823         /**
8824          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8825          for more information about the sides.
8826          * @param {String} sides
8827          * @return {Number}
8828          */
8829         getFrameWidth : function(sides, onlyContentBox){
8830             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8831         },
8832
8833         /**
8834          * 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.
8835          * @param {Object} box The box to fill {x, y, width, height}
8836          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8837          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8838          * @return {Roo.Element} this
8839          */
8840         setBox : function(box, adjust, animate){
8841             var w = box.width, h = box.height;
8842             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8843                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8844                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8845             }
8846             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8847             return this;
8848         },
8849
8850         /**
8851          * Forces the browser to repaint this element
8852          * @return {Roo.Element} this
8853          */
8854          repaint : function(){
8855             var dom = this.dom;
8856             this.addClass("x-repaint");
8857             setTimeout(function(){
8858                 Roo.get(dom).removeClass("x-repaint");
8859             }, 1);
8860             return this;
8861         },
8862
8863         /**
8864          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8865          * then it returns the calculated width of the sides (see getPadding)
8866          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8867          * @return {Object/Number}
8868          */
8869         getMargins : function(side){
8870             if(!side){
8871                 return {
8872                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8873                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8874                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8875                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8876                 };
8877             }else{
8878                 return this.addStyles(side, El.margins);
8879              }
8880         },
8881
8882         // private
8883         addStyles : function(sides, styles){
8884             var val = 0, v, w;
8885             for(var i = 0, len = sides.length; i < len; i++){
8886                 v = this.getStyle(styles[sides.charAt(i)]);
8887                 if(v){
8888                      w = parseInt(v, 10);
8889                      if(w){ val += w; }
8890                 }
8891             }
8892             return val;
8893         },
8894
8895         /**
8896          * Creates a proxy element of this element
8897          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8898          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8899          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8900          * @return {Roo.Element} The new proxy element
8901          */
8902         createProxy : function(config, renderTo, matchBox){
8903             if(renderTo){
8904                 renderTo = Roo.getDom(renderTo);
8905             }else{
8906                 renderTo = document.body;
8907             }
8908             config = typeof config == "object" ?
8909                 config : {tag : "div", cls: config};
8910             var proxy = Roo.DomHelper.append(renderTo, config, true);
8911             if(matchBox){
8912                proxy.setBox(this.getBox());
8913             }
8914             return proxy;
8915         },
8916
8917         /**
8918          * Puts a mask over this element to disable user interaction. Requires core.css.
8919          * This method can only be applied to elements which accept child nodes.
8920          * @param {String} msg (optional) A message to display in the mask
8921          * @param {String} msgCls (optional) A css class to apply to the msg element
8922          * @return {Element} The mask  element
8923          */
8924         mask : function(msg, msgCls)
8925         {
8926             if(this.getStyle("position") == "static"){
8927                 this.setStyle("position", "relative");
8928             }
8929             if(!this._mask){
8930                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8931             }
8932             this.addClass("x-masked");
8933             this._mask.setDisplayed(true);
8934             
8935             // we wander
8936             var z = 0;
8937             var dom = this.dom
8938             while (dom && dom.style) {
8939                 if (!isNaN(parseInt(dom.style.zIndex))) {
8940                     z = Math.max(z, parseInt(dom.style.zIndex));
8941                 }
8942                 dom = dom.parentNode;
8943             }
8944             // if we are masking the body - then it hides everything..
8945             if (this.dom == document.body) {
8946                 z = 1000000;
8947                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8948                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8949             }
8950            
8951             if(typeof msg == 'string'){
8952                 if(!this._maskMsg){
8953                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8954                 }
8955                 var mm = this._maskMsg;
8956                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8957                 mm.dom.firstChild.innerHTML = msg;
8958                 mm.setDisplayed(true);
8959                 mm.center(this);
8960                 mm.setStyle('z-index', z + 102);
8961             }
8962             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8963                 this._mask.setHeight(this.getHeight());
8964             }
8965             this._mask.setStyle('z-index', z + 100);
8966             
8967             return this._mask;
8968         },
8969
8970         /**
8971          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8972          * it is cached for reuse.
8973          */
8974         unmask : function(removeEl){
8975             if(this._mask){
8976                 if(removeEl === true){
8977                     this._mask.remove();
8978                     delete this._mask;
8979                     if(this._maskMsg){
8980                         this._maskMsg.remove();
8981                         delete this._maskMsg;
8982                     }
8983                 }else{
8984                     this._mask.setDisplayed(false);
8985                     if(this._maskMsg){
8986                         this._maskMsg.setDisplayed(false);
8987                     }
8988                 }
8989             }
8990             this.removeClass("x-masked");
8991         },
8992
8993         /**
8994          * Returns true if this element is masked
8995          * @return {Boolean}
8996          */
8997         isMasked : function(){
8998             return this._mask && this._mask.isVisible();
8999         },
9000
9001         /**
9002          * Creates an iframe shim for this element to keep selects and other windowed objects from
9003          * showing through.
9004          * @return {Roo.Element} The new shim element
9005          */
9006         createShim : function(){
9007             var el = document.createElement('iframe');
9008             el.frameBorder = 'no';
9009             el.className = 'roo-shim';
9010             if(Roo.isIE && Roo.isSecure){
9011                 el.src = Roo.SSL_SECURE_URL;
9012             }
9013             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9014             shim.autoBoxAdjust = false;
9015             return shim;
9016         },
9017
9018         /**
9019          * Removes this element from the DOM and deletes it from the cache
9020          */
9021         remove : function(){
9022             if(this.dom.parentNode){
9023                 this.dom.parentNode.removeChild(this.dom);
9024             }
9025             delete El.cache[this.dom.id];
9026         },
9027
9028         /**
9029          * Sets up event handlers to add and remove a css class when the mouse is over this element
9030          * @param {String} className
9031          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9032          * mouseout events for children elements
9033          * @return {Roo.Element} this
9034          */
9035         addClassOnOver : function(className, preventFlicker){
9036             this.on("mouseover", function(){
9037                 Roo.fly(this, '_internal').addClass(className);
9038             }, this.dom);
9039             var removeFn = function(e){
9040                 if(preventFlicker !== true || !e.within(this, true)){
9041                     Roo.fly(this, '_internal').removeClass(className);
9042                 }
9043             };
9044             this.on("mouseout", removeFn, this.dom);
9045             return this;
9046         },
9047
9048         /**
9049          * Sets up event handlers to add and remove a css class when this element has the focus
9050          * @param {String} className
9051          * @return {Roo.Element} this
9052          */
9053         addClassOnFocus : function(className){
9054             this.on("focus", function(){
9055                 Roo.fly(this, '_internal').addClass(className);
9056             }, this.dom);
9057             this.on("blur", function(){
9058                 Roo.fly(this, '_internal').removeClass(className);
9059             }, this.dom);
9060             return this;
9061         },
9062         /**
9063          * 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)
9064          * @param {String} className
9065          * @return {Roo.Element} this
9066          */
9067         addClassOnClick : function(className){
9068             var dom = this.dom;
9069             this.on("mousedown", function(){
9070                 Roo.fly(dom, '_internal').addClass(className);
9071                 var d = Roo.get(document);
9072                 var fn = function(){
9073                     Roo.fly(dom, '_internal').removeClass(className);
9074                     d.removeListener("mouseup", fn);
9075                 };
9076                 d.on("mouseup", fn);
9077             });
9078             return this;
9079         },
9080
9081         /**
9082          * Stops the specified event from bubbling and optionally prevents the default action
9083          * @param {String} eventName
9084          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9085          * @return {Roo.Element} this
9086          */
9087         swallowEvent : function(eventName, preventDefault){
9088             var fn = function(e){
9089                 e.stopPropagation();
9090                 if(preventDefault){
9091                     e.preventDefault();
9092                 }
9093             };
9094             if(eventName instanceof Array){
9095                 for(var i = 0, len = eventName.length; i < len; i++){
9096                      this.on(eventName[i], fn);
9097                 }
9098                 return this;
9099             }
9100             this.on(eventName, fn);
9101             return this;
9102         },
9103
9104         /**
9105          * @private
9106          */
9107       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9108
9109         /**
9110          * Sizes this element to its parent element's dimensions performing
9111          * neccessary box adjustments.
9112          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9113          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9114          * @return {Roo.Element} this
9115          */
9116         fitToParent : function(monitorResize, targetParent) {
9117           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9118           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9119           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9120             return;
9121           }
9122           var p = Roo.get(targetParent || this.dom.parentNode);
9123           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9124           if (monitorResize === true) {
9125             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9126             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9127           }
9128           return this;
9129         },
9130
9131         /**
9132          * Gets the next sibling, skipping text nodes
9133          * @return {HTMLElement} The next sibling or null
9134          */
9135         getNextSibling : function(){
9136             var n = this.dom.nextSibling;
9137             while(n && n.nodeType != 1){
9138                 n = n.nextSibling;
9139             }
9140             return n;
9141         },
9142
9143         /**
9144          * Gets the previous sibling, skipping text nodes
9145          * @return {HTMLElement} The previous sibling or null
9146          */
9147         getPrevSibling : function(){
9148             var n = this.dom.previousSibling;
9149             while(n && n.nodeType != 1){
9150                 n = n.previousSibling;
9151             }
9152             return n;
9153         },
9154
9155
9156         /**
9157          * Appends the passed element(s) to this element
9158          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9159          * @return {Roo.Element} this
9160          */
9161         appendChild: function(el){
9162             el = Roo.get(el);
9163             el.appendTo(this);
9164             return this;
9165         },
9166
9167         /**
9168          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9169          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9170          * automatically generated with the specified attributes.
9171          * @param {HTMLElement} insertBefore (optional) a child element of this element
9172          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9173          * @return {Roo.Element} The new child element
9174          */
9175         createChild: function(config, insertBefore, returnDom){
9176             config = config || {tag:'div'};
9177             if(insertBefore){
9178                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9179             }
9180             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9181         },
9182
9183         /**
9184          * Appends this element to the passed element
9185          * @param {String/HTMLElement/Element} el The new parent element
9186          * @return {Roo.Element} this
9187          */
9188         appendTo: function(el){
9189             el = Roo.getDom(el);
9190             el.appendChild(this.dom);
9191             return this;
9192         },
9193
9194         /**
9195          * Inserts this element before the passed element in the DOM
9196          * @param {String/HTMLElement/Element} el The element to insert before
9197          * @return {Roo.Element} this
9198          */
9199         insertBefore: function(el){
9200             el = Roo.getDom(el);
9201             el.parentNode.insertBefore(this.dom, el);
9202             return this;
9203         },
9204
9205         /**
9206          * Inserts this element after the passed element in the DOM
9207          * @param {String/HTMLElement/Element} el The element to insert after
9208          * @return {Roo.Element} this
9209          */
9210         insertAfter: function(el){
9211             el = Roo.getDom(el);
9212             el.parentNode.insertBefore(this.dom, el.nextSibling);
9213             return this;
9214         },
9215
9216         /**
9217          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9218          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9219          * @return {Roo.Element} The new child
9220          */
9221         insertFirst: function(el, returnDom){
9222             el = el || {};
9223             if(typeof el == 'object' && !el.nodeType){ // dh config
9224                 return this.createChild(el, this.dom.firstChild, returnDom);
9225             }else{
9226                 el = Roo.getDom(el);
9227                 this.dom.insertBefore(el, this.dom.firstChild);
9228                 return !returnDom ? Roo.get(el) : el;
9229             }
9230         },
9231
9232         /**
9233          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9234          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9235          * @param {String} where (optional) 'before' or 'after' defaults to before
9236          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9237          * @return {Roo.Element} the inserted Element
9238          */
9239         insertSibling: function(el, where, returnDom){
9240             where = where ? where.toLowerCase() : 'before';
9241             el = el || {};
9242             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9243
9244             if(typeof el == 'object' && !el.nodeType){ // dh config
9245                 if(where == 'after' && !this.dom.nextSibling){
9246                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9247                 }else{
9248                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9249                 }
9250
9251             }else{
9252                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9253                             where == 'before' ? this.dom : this.dom.nextSibling);
9254                 if(!returnDom){
9255                     rt = Roo.get(rt);
9256                 }
9257             }
9258             return rt;
9259         },
9260
9261         /**
9262          * Creates and wraps this element with another element
9263          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9264          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9265          * @return {HTMLElement/Element} The newly created wrapper element
9266          */
9267         wrap: function(config, returnDom){
9268             if(!config){
9269                 config = {tag: "div"};
9270             }
9271             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9272             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9273             return newEl;
9274         },
9275
9276         /**
9277          * Replaces the passed element with this element
9278          * @param {String/HTMLElement/Element} el The element to replace
9279          * @return {Roo.Element} this
9280          */
9281         replace: function(el){
9282             el = Roo.get(el);
9283             this.insertBefore(el);
9284             el.remove();
9285             return this;
9286         },
9287
9288         /**
9289          * Inserts an html fragment into this element
9290          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9291          * @param {String} html The HTML fragment
9292          * @param {Boolean} returnEl True to return an Roo.Element
9293          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9294          */
9295         insertHtml : function(where, html, returnEl){
9296             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9297             return returnEl ? Roo.get(el) : el;
9298         },
9299
9300         /**
9301          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9302          * @param {Object} o The object with the attributes
9303          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9304          * @return {Roo.Element} this
9305          */
9306         set : function(o, useSet){
9307             var el = this.dom;
9308             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9309             for(var attr in o){
9310                 if(attr == "style" || typeof o[attr] == "function") continue;
9311                 if(attr=="cls"){
9312                     el.className = o["cls"];
9313                 }else{
9314                     if(useSet) el.setAttribute(attr, o[attr]);
9315                     else el[attr] = o[attr];
9316                 }
9317             }
9318             if(o.style){
9319                 Roo.DomHelper.applyStyles(el, o.style);
9320             }
9321             return this;
9322         },
9323
9324         /**
9325          * Convenience method for constructing a KeyMap
9326          * @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:
9327          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9328          * @param {Function} fn The function to call
9329          * @param {Object} scope (optional) The scope of the function
9330          * @return {Roo.KeyMap} The KeyMap created
9331          */
9332         addKeyListener : function(key, fn, scope){
9333             var config;
9334             if(typeof key != "object" || key instanceof Array){
9335                 config = {
9336                     key: key,
9337                     fn: fn,
9338                     scope: scope
9339                 };
9340             }else{
9341                 config = {
9342                     key : key.key,
9343                     shift : key.shift,
9344                     ctrl : key.ctrl,
9345                     alt : key.alt,
9346                     fn: fn,
9347                     scope: scope
9348                 };
9349             }
9350             return new Roo.KeyMap(this, config);
9351         },
9352
9353         /**
9354          * Creates a KeyMap for this element
9355          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9356          * @return {Roo.KeyMap} The KeyMap created
9357          */
9358         addKeyMap : function(config){
9359             return new Roo.KeyMap(this, config);
9360         },
9361
9362         /**
9363          * Returns true if this element is scrollable.
9364          * @return {Boolean}
9365          */
9366          isScrollable : function(){
9367             var dom = this.dom;
9368             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9369         },
9370
9371         /**
9372          * 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().
9373          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9374          * @param {Number} value The new scroll value
9375          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9376          * @return {Element} this
9377          */
9378
9379         scrollTo : function(side, value, animate){
9380             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9381             if(!animate || !A){
9382                 this.dom[prop] = value;
9383             }else{
9384                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9385                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9386             }
9387             return this;
9388         },
9389
9390         /**
9391          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9392          * within this element's scrollable range.
9393          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9394          * @param {Number} distance How far to scroll the element in pixels
9395          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9396          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9397          * was scrolled as far as it could go.
9398          */
9399          scroll : function(direction, distance, animate){
9400              if(!this.isScrollable()){
9401                  return;
9402              }
9403              var el = this.dom;
9404              var l = el.scrollLeft, t = el.scrollTop;
9405              var w = el.scrollWidth, h = el.scrollHeight;
9406              var cw = el.clientWidth, ch = el.clientHeight;
9407              direction = direction.toLowerCase();
9408              var scrolled = false;
9409              var a = this.preanim(arguments, 2);
9410              switch(direction){
9411                  case "l":
9412                  case "left":
9413                      if(w - l > cw){
9414                          var v = Math.min(l + distance, w-cw);
9415                          this.scrollTo("left", v, a);
9416                          scrolled = true;
9417                      }
9418                      break;
9419                 case "r":
9420                 case "right":
9421                      if(l > 0){
9422                          var v = Math.max(l - distance, 0);
9423                          this.scrollTo("left", v, a);
9424                          scrolled = true;
9425                      }
9426                      break;
9427                 case "t":
9428                 case "top":
9429                 case "up":
9430                      if(t > 0){
9431                          var v = Math.max(t - distance, 0);
9432                          this.scrollTo("top", v, a);
9433                          scrolled = true;
9434                      }
9435                      break;
9436                 case "b":
9437                 case "bottom":
9438                 case "down":
9439                      if(h - t > ch){
9440                          var v = Math.min(t + distance, h-ch);
9441                          this.scrollTo("top", v, a);
9442                          scrolled = true;
9443                      }
9444                      break;
9445              }
9446              return scrolled;
9447         },
9448
9449         /**
9450          * Translates the passed page coordinates into left/top css values for this element
9451          * @param {Number/Array} x The page x or an array containing [x, y]
9452          * @param {Number} y The page y
9453          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9454          */
9455         translatePoints : function(x, y){
9456             if(typeof x == 'object' || x instanceof Array){
9457                 y = x[1]; x = x[0];
9458             }
9459             var p = this.getStyle('position');
9460             var o = this.getXY();
9461
9462             var l = parseInt(this.getStyle('left'), 10);
9463             var t = parseInt(this.getStyle('top'), 10);
9464
9465             if(isNaN(l)){
9466                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9467             }
9468             if(isNaN(t)){
9469                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9470             }
9471
9472             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9473         },
9474
9475         /**
9476          * Returns the current scroll position of the element.
9477          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9478          */
9479         getScroll : function(){
9480             var d = this.dom, doc = document;
9481             if(d == doc || d == doc.body){
9482                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9483                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9484                 return {left: l, top: t};
9485             }else{
9486                 return {left: d.scrollLeft, top: d.scrollTop};
9487             }
9488         },
9489
9490         /**
9491          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9492          * are convert to standard 6 digit hex color.
9493          * @param {String} attr The css attribute
9494          * @param {String} defaultValue The default value to use when a valid color isn't found
9495          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9496          * YUI color anims.
9497          */
9498         getColor : function(attr, defaultValue, prefix){
9499             var v = this.getStyle(attr);
9500             if(!v || v == "transparent" || v == "inherit") {
9501                 return defaultValue;
9502             }
9503             var color = typeof prefix == "undefined" ? "#" : prefix;
9504             if(v.substr(0, 4) == "rgb("){
9505                 var rvs = v.slice(4, v.length -1).split(",");
9506                 for(var i = 0; i < 3; i++){
9507                     var h = parseInt(rvs[i]).toString(16);
9508                     if(h < 16){
9509                         h = "0" + h;
9510                     }
9511                     color += h;
9512                 }
9513             } else {
9514                 if(v.substr(0, 1) == "#"){
9515                     if(v.length == 4) {
9516                         for(var i = 1; i < 4; i++){
9517                             var c = v.charAt(i);
9518                             color +=  c + c;
9519                         }
9520                     }else if(v.length == 7){
9521                         color += v.substr(1);
9522                     }
9523                 }
9524             }
9525             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9526         },
9527
9528         /**
9529          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9530          * gradient background, rounded corners and a 4-way shadow.
9531          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9532          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9533          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9534          * @return {Roo.Element} this
9535          */
9536         boxWrap : function(cls){
9537             cls = cls || 'x-box';
9538             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9539             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9540             return el;
9541         },
9542
9543         /**
9544          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9545          * @param {String} namespace The namespace in which to look for the attribute
9546          * @param {String} name The attribute name
9547          * @return {String} The attribute value
9548          */
9549         getAttributeNS : Roo.isIE ? function(ns, name){
9550             var d = this.dom;
9551             var type = typeof d[ns+":"+name];
9552             if(type != 'undefined' && type != 'unknown'){
9553                 return d[ns+":"+name];
9554             }
9555             return d[name];
9556         } : function(ns, name){
9557             var d = this.dom;
9558             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9559         },
9560         
9561         
9562         /**
9563          * Sets or Returns the value the dom attribute value
9564          * @param {String} name The attribute name
9565          * @param {String} value (optional) The value to set the attribute to
9566          * @return {String} The attribute value
9567          */
9568         attr : function(name){
9569             if (arguments.length > 1) {
9570                 this.dom.setAttribute(name, arguments[1]);
9571                 return arguments[1];
9572             }
9573             if (!this.dom.hasAttribute(name)) {
9574                 return undefined;
9575             }
9576             return this.dom.getAttribute(name);
9577         }
9578         
9579         
9580         
9581     };
9582
9583     var ep = El.prototype;
9584
9585     /**
9586      * Appends an event handler (Shorthand for addListener)
9587      * @param {String}   eventName     The type of event to append
9588      * @param {Function} fn        The method the event invokes
9589      * @param {Object} scope       (optional) The scope (this object) of the fn
9590      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9591      * @method
9592      */
9593     ep.on = ep.addListener;
9594         // backwards compat
9595     ep.mon = ep.addListener;
9596
9597     /**
9598      * Removes an event handler from this element (shorthand for removeListener)
9599      * @param {String} eventName the type of event to remove
9600      * @param {Function} fn the method the event invokes
9601      * @return {Roo.Element} this
9602      * @method
9603      */
9604     ep.un = ep.removeListener;
9605
9606     /**
9607      * true to automatically adjust width and height settings for box-model issues (default to true)
9608      */
9609     ep.autoBoxAdjust = true;
9610
9611     // private
9612     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9613
9614     // private
9615     El.addUnits = function(v, defaultUnit){
9616         if(v === "" || v == "auto"){
9617             return v;
9618         }
9619         if(v === undefined){
9620             return '';
9621         }
9622         if(typeof v == "number" || !El.unitPattern.test(v)){
9623             return v + (defaultUnit || 'px');
9624         }
9625         return v;
9626     };
9627
9628     // special markup used throughout Roo when box wrapping elements
9629     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>';
9630     /**
9631      * Visibility mode constant - Use visibility to hide element
9632      * @static
9633      * @type Number
9634      */
9635     El.VISIBILITY = 1;
9636     /**
9637      * Visibility mode constant - Use display to hide element
9638      * @static
9639      * @type Number
9640      */
9641     El.DISPLAY = 2;
9642
9643     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9644     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9645     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9646
9647
9648
9649     /**
9650      * @private
9651      */
9652     El.cache = {};
9653
9654     var docEl;
9655
9656     /**
9657      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9658      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9659      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9660      * @return {Element} The Element object
9661      * @static
9662      */
9663     El.get = function(el){
9664         var ex, elm, id;
9665         if(!el){ return null; }
9666         if(typeof el == "string"){ // element id
9667             if(!(elm = document.getElementById(el))){
9668                 return null;
9669             }
9670             if(ex = El.cache[el]){
9671                 ex.dom = elm;
9672             }else{
9673                 ex = El.cache[el] = new El(elm);
9674             }
9675             return ex;
9676         }else if(el.tagName){ // dom element
9677             if(!(id = el.id)){
9678                 id = Roo.id(el);
9679             }
9680             if(ex = El.cache[id]){
9681                 ex.dom = el;
9682             }else{
9683                 ex = El.cache[id] = new El(el);
9684             }
9685             return ex;
9686         }else if(el instanceof El){
9687             if(el != docEl){
9688                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9689                                                               // catch case where it hasn't been appended
9690                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9691             }
9692             return el;
9693         }else if(el.isComposite){
9694             return el;
9695         }else if(el instanceof Array){
9696             return El.select(el);
9697         }else if(el == document){
9698             // create a bogus element object representing the document object
9699             if(!docEl){
9700                 var f = function(){};
9701                 f.prototype = El.prototype;
9702                 docEl = new f();
9703                 docEl.dom = document;
9704             }
9705             return docEl;
9706         }
9707         return null;
9708     };
9709
9710     // private
9711     El.uncache = function(el){
9712         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9713             if(a[i]){
9714                 delete El.cache[a[i].id || a[i]];
9715             }
9716         }
9717     };
9718
9719     // private
9720     // Garbage collection - uncache elements/purge listeners on orphaned elements
9721     // so we don't hold a reference and cause the browser to retain them
9722     El.garbageCollect = function(){
9723         if(!Roo.enableGarbageCollector){
9724             clearInterval(El.collectorThread);
9725             return;
9726         }
9727         for(var eid in El.cache){
9728             var el = El.cache[eid], d = el.dom;
9729             // -------------------------------------------------------
9730             // Determining what is garbage:
9731             // -------------------------------------------------------
9732             // !d
9733             // dom node is null, definitely garbage
9734             // -------------------------------------------------------
9735             // !d.parentNode
9736             // no parentNode == direct orphan, definitely garbage
9737             // -------------------------------------------------------
9738             // !d.offsetParent && !document.getElementById(eid)
9739             // display none elements have no offsetParent so we will
9740             // also try to look it up by it's id. However, check
9741             // offsetParent first so we don't do unneeded lookups.
9742             // This enables collection of elements that are not orphans
9743             // directly, but somewhere up the line they have an orphan
9744             // parent.
9745             // -------------------------------------------------------
9746             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9747                 delete El.cache[eid];
9748                 if(d && Roo.enableListenerCollection){
9749                     E.purgeElement(d);
9750                 }
9751             }
9752         }
9753     }
9754     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9755
9756
9757     // dom is optional
9758     El.Flyweight = function(dom){
9759         this.dom = dom;
9760     };
9761     El.Flyweight.prototype = El.prototype;
9762
9763     El._flyweights = {};
9764     /**
9765      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9766      * the dom node can be overwritten by other code.
9767      * @param {String/HTMLElement} el The dom node or id
9768      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9769      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9770      * @static
9771      * @return {Element} The shared Element object
9772      */
9773     El.fly = function(el, named){
9774         named = named || '_global';
9775         el = Roo.getDom(el);
9776         if(!el){
9777             return null;
9778         }
9779         if(!El._flyweights[named]){
9780             El._flyweights[named] = new El.Flyweight();
9781         }
9782         El._flyweights[named].dom = el;
9783         return El._flyweights[named];
9784     };
9785
9786     /**
9787      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9788      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9789      * Shorthand of {@link Roo.Element#get}
9790      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9791      * @return {Element} The Element object
9792      * @member Roo
9793      * @method get
9794      */
9795     Roo.get = El.get;
9796     /**
9797      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9798      * the dom node can be overwritten by other code.
9799      * Shorthand of {@link Roo.Element#fly}
9800      * @param {String/HTMLElement} el The dom node or id
9801      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9802      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9803      * @static
9804      * @return {Element} The shared Element object
9805      * @member Roo
9806      * @method fly
9807      */
9808     Roo.fly = El.fly;
9809
9810     // speedy lookup for elements never to box adjust
9811     var noBoxAdjust = Roo.isStrict ? {
9812         select:1
9813     } : {
9814         input:1, select:1, textarea:1
9815     };
9816     if(Roo.isIE || Roo.isGecko){
9817         noBoxAdjust['button'] = 1;
9818     }
9819
9820
9821     Roo.EventManager.on(window, 'unload', function(){
9822         delete El.cache;
9823         delete El._flyweights;
9824     });
9825 })();
9826
9827
9828
9829
9830 if(Roo.DomQuery){
9831     Roo.Element.selectorFunction = Roo.DomQuery.select;
9832 }
9833
9834 Roo.Element.select = function(selector, unique, root){
9835     var els;
9836     if(typeof selector == "string"){
9837         els = Roo.Element.selectorFunction(selector, root);
9838     }else if(selector.length !== undefined){
9839         els = selector;
9840     }else{
9841         throw "Invalid selector";
9842     }
9843     if(unique === true){
9844         return new Roo.CompositeElement(els);
9845     }else{
9846         return new Roo.CompositeElementLite(els);
9847     }
9848 };
9849 /**
9850  * Selects elements based on the passed CSS selector to enable working on them as 1.
9851  * @param {String/Array} selector The CSS selector or an array of elements
9852  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9853  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9854  * @return {CompositeElementLite/CompositeElement}
9855  * @member Roo
9856  * @method select
9857  */
9858 Roo.select = Roo.Element.select;
9859
9860
9861
9862
9863
9864
9865
9866
9867
9868
9869
9870
9871
9872
9873 /*
9874  * Based on:
9875  * Ext JS Library 1.1.1
9876  * Copyright(c) 2006-2007, Ext JS, LLC.
9877  *
9878  * Originally Released Under LGPL - original licence link has changed is not relivant.
9879  *
9880  * Fork - LGPL
9881  * <script type="text/javascript">
9882  */
9883
9884
9885
9886 //Notifies Element that fx methods are available
9887 Roo.enableFx = true;
9888
9889 /**
9890  * @class Roo.Fx
9891  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9892  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9893  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9894  * Element effects to work.</p><br/>
9895  *
9896  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9897  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9898  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9899  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9900  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9901  * expected results and should be done with care.</p><br/>
9902  *
9903  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9904  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9905 <pre>
9906 Value  Description
9907 -----  -----------------------------
9908 tl     The top left corner
9909 t      The center of the top edge
9910 tr     The top right corner
9911 l      The center of the left edge
9912 r      The center of the right edge
9913 bl     The bottom left corner
9914 b      The center of the bottom edge
9915 br     The bottom right corner
9916 </pre>
9917  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9918  * below are common options that can be passed to any Fx method.</b>
9919  * @cfg {Function} callback A function called when the effect is finished
9920  * @cfg {Object} scope The scope of the effect function
9921  * @cfg {String} easing A valid Easing value for the effect
9922  * @cfg {String} afterCls A css class to apply after the effect
9923  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9924  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9925  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9926  * effects that end with the element being visually hidden, ignored otherwise)
9927  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9928  * a function which returns such a specification that will be applied to the Element after the effect finishes
9929  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9930  * @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
9931  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9932  */
9933 Roo.Fx = {
9934         /**
9935          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9936          * origin for the slide effect.  This function automatically handles wrapping the element with
9937          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9938          * Usage:
9939          *<pre><code>
9940 // default: slide the element in from the top
9941 el.slideIn();
9942
9943 // custom: slide the element in from the right with a 2-second duration
9944 el.slideIn('r', { duration: 2 });
9945
9946 // common config options shown with default values
9947 el.slideIn('t', {
9948     easing: 'easeOut',
9949     duration: .5
9950 });
9951 </code></pre>
9952          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9953          * @param {Object} options (optional) Object literal with any of the Fx config options
9954          * @return {Roo.Element} The Element
9955          */
9956     slideIn : function(anchor, o){
9957         var el = this.getFxEl();
9958         o = o || {};
9959
9960         el.queueFx(o, function(){
9961
9962             anchor = anchor || "t";
9963
9964             // fix display to visibility
9965             this.fixDisplay();
9966
9967             // restore values after effect
9968             var r = this.getFxRestore();
9969             var b = this.getBox();
9970             // fixed size for slide
9971             this.setSize(b);
9972
9973             // wrap if needed
9974             var wrap = this.fxWrap(r.pos, o, "hidden");
9975
9976             var st = this.dom.style;
9977             st.visibility = "visible";
9978             st.position = "absolute";
9979
9980             // clear out temp styles after slide and unwrap
9981             var after = function(){
9982                 el.fxUnwrap(wrap, r.pos, o);
9983                 st.width = r.width;
9984                 st.height = r.height;
9985                 el.afterFx(o);
9986             };
9987             // time to calc the positions
9988             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9989
9990             switch(anchor.toLowerCase()){
9991                 case "t":
9992                     wrap.setSize(b.width, 0);
9993                     st.left = st.bottom = "0";
9994                     a = {height: bh};
9995                 break;
9996                 case "l":
9997                     wrap.setSize(0, b.height);
9998                     st.right = st.top = "0";
9999                     a = {width: bw};
10000                 break;
10001                 case "r":
10002                     wrap.setSize(0, b.height);
10003                     wrap.setX(b.right);
10004                     st.left = st.top = "0";
10005                     a = {width: bw, points: pt};
10006                 break;
10007                 case "b":
10008                     wrap.setSize(b.width, 0);
10009                     wrap.setY(b.bottom);
10010                     st.left = st.top = "0";
10011                     a = {height: bh, points: pt};
10012                 break;
10013                 case "tl":
10014                     wrap.setSize(0, 0);
10015                     st.right = st.bottom = "0";
10016                     a = {width: bw, height: bh};
10017                 break;
10018                 case "bl":
10019                     wrap.setSize(0, 0);
10020                     wrap.setY(b.y+b.height);
10021                     st.right = st.top = "0";
10022                     a = {width: bw, height: bh, points: pt};
10023                 break;
10024                 case "br":
10025                     wrap.setSize(0, 0);
10026                     wrap.setXY([b.right, b.bottom]);
10027                     st.left = st.top = "0";
10028                     a = {width: bw, height: bh, points: pt};
10029                 break;
10030                 case "tr":
10031                     wrap.setSize(0, 0);
10032                     wrap.setX(b.x+b.width);
10033                     st.left = st.bottom = "0";
10034                     a = {width: bw, height: bh, points: pt};
10035                 break;
10036             }
10037             this.dom.style.visibility = "visible";
10038             wrap.show();
10039
10040             arguments.callee.anim = wrap.fxanim(a,
10041                 o,
10042                 'motion',
10043                 .5,
10044                 'easeOut', after);
10045         });
10046         return this;
10047     },
10048     
10049         /**
10050          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10051          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10052          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10053          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10054          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10055          * Usage:
10056          *<pre><code>
10057 // default: slide the element out to the top
10058 el.slideOut();
10059
10060 // custom: slide the element out to the right with a 2-second duration
10061 el.slideOut('r', { duration: 2 });
10062
10063 // common config options shown with default values
10064 el.slideOut('t', {
10065     easing: 'easeOut',
10066     duration: .5,
10067     remove: false,
10068     useDisplay: false
10069 });
10070 </code></pre>
10071          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10072          * @param {Object} options (optional) Object literal with any of the Fx config options
10073          * @return {Roo.Element} The Element
10074          */
10075     slideOut : function(anchor, o){
10076         var el = this.getFxEl();
10077         o = o || {};
10078
10079         el.queueFx(o, function(){
10080
10081             anchor = anchor || "t";
10082
10083             // restore values after effect
10084             var r = this.getFxRestore();
10085             
10086             var b = this.getBox();
10087             // fixed size for slide
10088             this.setSize(b);
10089
10090             // wrap if needed
10091             var wrap = this.fxWrap(r.pos, o, "visible");
10092
10093             var st = this.dom.style;
10094             st.visibility = "visible";
10095             st.position = "absolute";
10096
10097             wrap.setSize(b);
10098
10099             var after = function(){
10100                 if(o.useDisplay){
10101                     el.setDisplayed(false);
10102                 }else{
10103                     el.hide();
10104                 }
10105
10106                 el.fxUnwrap(wrap, r.pos, o);
10107
10108                 st.width = r.width;
10109                 st.height = r.height;
10110
10111                 el.afterFx(o);
10112             };
10113
10114             var a, zero = {to: 0};
10115             switch(anchor.toLowerCase()){
10116                 case "t":
10117                     st.left = st.bottom = "0";
10118                     a = {height: zero};
10119                 break;
10120                 case "l":
10121                     st.right = st.top = "0";
10122                     a = {width: zero};
10123                 break;
10124                 case "r":
10125                     st.left = st.top = "0";
10126                     a = {width: zero, points: {to:[b.right, b.y]}};
10127                 break;
10128                 case "b":
10129                     st.left = st.top = "0";
10130                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10131                 break;
10132                 case "tl":
10133                     st.right = st.bottom = "0";
10134                     a = {width: zero, height: zero};
10135                 break;
10136                 case "bl":
10137                     st.right = st.top = "0";
10138                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10139                 break;
10140                 case "br":
10141                     st.left = st.top = "0";
10142                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10143                 break;
10144                 case "tr":
10145                     st.left = st.bottom = "0";
10146                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10147                 break;
10148             }
10149
10150             arguments.callee.anim = wrap.fxanim(a,
10151                 o,
10152                 'motion',
10153                 .5,
10154                 "easeOut", after);
10155         });
10156         return this;
10157     },
10158
10159         /**
10160          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10161          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10162          * The element must be removed from the DOM using the 'remove' config option if desired.
10163          * Usage:
10164          *<pre><code>
10165 // default
10166 el.puff();
10167
10168 // common config options shown with default values
10169 el.puff({
10170     easing: 'easeOut',
10171     duration: .5,
10172     remove: false,
10173     useDisplay: false
10174 });
10175 </code></pre>
10176          * @param {Object} options (optional) Object literal with any of the Fx config options
10177          * @return {Roo.Element} The Element
10178          */
10179     puff : function(o){
10180         var el = this.getFxEl();
10181         o = o || {};
10182
10183         el.queueFx(o, function(){
10184             this.clearOpacity();
10185             this.show();
10186
10187             // restore values after effect
10188             var r = this.getFxRestore();
10189             var st = this.dom.style;
10190
10191             var after = function(){
10192                 if(o.useDisplay){
10193                     el.setDisplayed(false);
10194                 }else{
10195                     el.hide();
10196                 }
10197
10198                 el.clearOpacity();
10199
10200                 el.setPositioning(r.pos);
10201                 st.width = r.width;
10202                 st.height = r.height;
10203                 st.fontSize = '';
10204                 el.afterFx(o);
10205             };
10206
10207             var width = this.getWidth();
10208             var height = this.getHeight();
10209
10210             arguments.callee.anim = this.fxanim({
10211                     width : {to: this.adjustWidth(width * 2)},
10212                     height : {to: this.adjustHeight(height * 2)},
10213                     points : {by: [-(width * .5), -(height * .5)]},
10214                     opacity : {to: 0},
10215                     fontSize: {to:200, unit: "%"}
10216                 },
10217                 o,
10218                 'motion',
10219                 .5,
10220                 "easeOut", after);
10221         });
10222         return this;
10223     },
10224
10225         /**
10226          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10227          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10228          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10229          * Usage:
10230          *<pre><code>
10231 // default
10232 el.switchOff();
10233
10234 // all config options shown with default values
10235 el.switchOff({
10236     easing: 'easeIn',
10237     duration: .3,
10238     remove: false,
10239     useDisplay: false
10240 });
10241 </code></pre>
10242          * @param {Object} options (optional) Object literal with any of the Fx config options
10243          * @return {Roo.Element} The Element
10244          */
10245     switchOff : function(o){
10246         var el = this.getFxEl();
10247         o = o || {};
10248
10249         el.queueFx(o, function(){
10250             this.clearOpacity();
10251             this.clip();
10252
10253             // restore values after effect
10254             var r = this.getFxRestore();
10255             var st = this.dom.style;
10256
10257             var after = function(){
10258                 if(o.useDisplay){
10259                     el.setDisplayed(false);
10260                 }else{
10261                     el.hide();
10262                 }
10263
10264                 el.clearOpacity();
10265                 el.setPositioning(r.pos);
10266                 st.width = r.width;
10267                 st.height = r.height;
10268
10269                 el.afterFx(o);
10270             };
10271
10272             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10273                 this.clearOpacity();
10274                 (function(){
10275                     this.fxanim({
10276                         height:{to:1},
10277                         points:{by:[0, this.getHeight() * .5]}
10278                     }, o, 'motion', 0.3, 'easeIn', after);
10279                 }).defer(100, this);
10280             });
10281         });
10282         return this;
10283     },
10284
10285     /**
10286      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10287      * changed using the "attr" config option) and then fading back to the original color. If no original
10288      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10289      * Usage:
10290 <pre><code>
10291 // default: highlight background to yellow
10292 el.highlight();
10293
10294 // custom: highlight foreground text to blue for 2 seconds
10295 el.highlight("0000ff", { attr: 'color', duration: 2 });
10296
10297 // common config options shown with default values
10298 el.highlight("ffff9c", {
10299     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10300     endColor: (current color) or "ffffff",
10301     easing: 'easeIn',
10302     duration: 1
10303 });
10304 </code></pre>
10305      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10306      * @param {Object} options (optional) Object literal with any of the Fx config options
10307      * @return {Roo.Element} The Element
10308      */ 
10309     highlight : function(color, o){
10310         var el = this.getFxEl();
10311         o = o || {};
10312
10313         el.queueFx(o, function(){
10314             color = color || "ffff9c";
10315             attr = o.attr || "backgroundColor";
10316
10317             this.clearOpacity();
10318             this.show();
10319
10320             var origColor = this.getColor(attr);
10321             var restoreColor = this.dom.style[attr];
10322             endColor = (o.endColor || origColor) || "ffffff";
10323
10324             var after = function(){
10325                 el.dom.style[attr] = restoreColor;
10326                 el.afterFx(o);
10327             };
10328
10329             var a = {};
10330             a[attr] = {from: color, to: endColor};
10331             arguments.callee.anim = this.fxanim(a,
10332                 o,
10333                 'color',
10334                 1,
10335                 'easeIn', after);
10336         });
10337         return this;
10338     },
10339
10340    /**
10341     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10342     * Usage:
10343 <pre><code>
10344 // default: a single light blue ripple
10345 el.frame();
10346
10347 // custom: 3 red ripples lasting 3 seconds total
10348 el.frame("ff0000", 3, { duration: 3 });
10349
10350 // common config options shown with default values
10351 el.frame("C3DAF9", 1, {
10352     duration: 1 //duration of entire animation (not each individual ripple)
10353     // Note: Easing is not configurable and will be ignored if included
10354 });
10355 </code></pre>
10356     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10357     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10358     * @param {Object} options (optional) Object literal with any of the Fx config options
10359     * @return {Roo.Element} The Element
10360     */
10361     frame : function(color, count, o){
10362         var el = this.getFxEl();
10363         o = o || {};
10364
10365         el.queueFx(o, function(){
10366             color = color || "#C3DAF9";
10367             if(color.length == 6){
10368                 color = "#" + color;
10369             }
10370             count = count || 1;
10371             duration = o.duration || 1;
10372             this.show();
10373
10374             var b = this.getBox();
10375             var animFn = function(){
10376                 var proxy = this.createProxy({
10377
10378                      style:{
10379                         visbility:"hidden",
10380                         position:"absolute",
10381                         "z-index":"35000", // yee haw
10382                         border:"0px solid " + color
10383                      }
10384                   });
10385                 var scale = Roo.isBorderBox ? 2 : 1;
10386                 proxy.animate({
10387                     top:{from:b.y, to:b.y - 20},
10388                     left:{from:b.x, to:b.x - 20},
10389                     borderWidth:{from:0, to:10},
10390                     opacity:{from:1, to:0},
10391                     height:{from:b.height, to:(b.height + (20*scale))},
10392                     width:{from:b.width, to:(b.width + (20*scale))}
10393                 }, duration, function(){
10394                     proxy.remove();
10395                 });
10396                 if(--count > 0){
10397                      animFn.defer((duration/2)*1000, this);
10398                 }else{
10399                     el.afterFx(o);
10400                 }
10401             };
10402             animFn.call(this);
10403         });
10404         return this;
10405     },
10406
10407    /**
10408     * Creates a pause before any subsequent queued effects begin.  If there are
10409     * no effects queued after the pause it will have no effect.
10410     * Usage:
10411 <pre><code>
10412 el.pause(1);
10413 </code></pre>
10414     * @param {Number} seconds The length of time to pause (in seconds)
10415     * @return {Roo.Element} The Element
10416     */
10417     pause : function(seconds){
10418         var el = this.getFxEl();
10419         var o = {};
10420
10421         el.queueFx(o, function(){
10422             setTimeout(function(){
10423                 el.afterFx(o);
10424             }, seconds * 1000);
10425         });
10426         return this;
10427     },
10428
10429    /**
10430     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10431     * using the "endOpacity" config option.
10432     * Usage:
10433 <pre><code>
10434 // default: fade in from opacity 0 to 100%
10435 el.fadeIn();
10436
10437 // custom: fade in from opacity 0 to 75% over 2 seconds
10438 el.fadeIn({ endOpacity: .75, duration: 2});
10439
10440 // common config options shown with default values
10441 el.fadeIn({
10442     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10443     easing: 'easeOut',
10444     duration: .5
10445 });
10446 </code></pre>
10447     * @param {Object} options (optional) Object literal with any of the Fx config options
10448     * @return {Roo.Element} The Element
10449     */
10450     fadeIn : function(o){
10451         var el = this.getFxEl();
10452         o = o || {};
10453         el.queueFx(o, function(){
10454             this.setOpacity(0);
10455             this.fixDisplay();
10456             this.dom.style.visibility = 'visible';
10457             var to = o.endOpacity || 1;
10458             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10459                 o, null, .5, "easeOut", function(){
10460                 if(to == 1){
10461                     this.clearOpacity();
10462                 }
10463                 el.afterFx(o);
10464             });
10465         });
10466         return this;
10467     },
10468
10469    /**
10470     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10471     * using the "endOpacity" config option.
10472     * Usage:
10473 <pre><code>
10474 // default: fade out from the element's current opacity to 0
10475 el.fadeOut();
10476
10477 // custom: fade out from the element's current opacity to 25% over 2 seconds
10478 el.fadeOut({ endOpacity: .25, duration: 2});
10479
10480 // common config options shown with default values
10481 el.fadeOut({
10482     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10483     easing: 'easeOut',
10484     duration: .5
10485     remove: false,
10486     useDisplay: false
10487 });
10488 </code></pre>
10489     * @param {Object} options (optional) Object literal with any of the Fx config options
10490     * @return {Roo.Element} The Element
10491     */
10492     fadeOut : function(o){
10493         var el = this.getFxEl();
10494         o = o || {};
10495         el.queueFx(o, function(){
10496             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10497                 o, null, .5, "easeOut", function(){
10498                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10499                      this.dom.style.display = "none";
10500                 }else{
10501                      this.dom.style.visibility = "hidden";
10502                 }
10503                 this.clearOpacity();
10504                 el.afterFx(o);
10505             });
10506         });
10507         return this;
10508     },
10509
10510    /**
10511     * Animates the transition of an element's dimensions from a starting height/width
10512     * to an ending height/width.
10513     * Usage:
10514 <pre><code>
10515 // change height and width to 100x100 pixels
10516 el.scale(100, 100);
10517
10518 // common config options shown with default values.  The height and width will default to
10519 // the element's existing values if passed as null.
10520 el.scale(
10521     [element's width],
10522     [element's height], {
10523     easing: 'easeOut',
10524     duration: .35
10525 });
10526 </code></pre>
10527     * @param {Number} width  The new width (pass undefined to keep the original width)
10528     * @param {Number} height  The new height (pass undefined to keep the original height)
10529     * @param {Object} options (optional) Object literal with any of the Fx config options
10530     * @return {Roo.Element} The Element
10531     */
10532     scale : function(w, h, o){
10533         this.shift(Roo.apply({}, o, {
10534             width: w,
10535             height: h
10536         }));
10537         return this;
10538     },
10539
10540    /**
10541     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10542     * Any of these properties not specified in the config object will not be changed.  This effect 
10543     * requires that at least one new dimension, position or opacity setting must be passed in on
10544     * the config object in order for the function to have any effect.
10545     * Usage:
10546 <pre><code>
10547 // slide the element horizontally to x position 200 while changing the height and opacity
10548 el.shift({ x: 200, height: 50, opacity: .8 });
10549
10550 // common config options shown with default values.
10551 el.shift({
10552     width: [element's width],
10553     height: [element's height],
10554     x: [element's x position],
10555     y: [element's y position],
10556     opacity: [element's opacity],
10557     easing: 'easeOut',
10558     duration: .35
10559 });
10560 </code></pre>
10561     * @param {Object} options  Object literal with any of the Fx config options
10562     * @return {Roo.Element} The Element
10563     */
10564     shift : function(o){
10565         var el = this.getFxEl();
10566         o = o || {};
10567         el.queueFx(o, function(){
10568             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10569             if(w !== undefined){
10570                 a.width = {to: this.adjustWidth(w)};
10571             }
10572             if(h !== undefined){
10573                 a.height = {to: this.adjustHeight(h)};
10574             }
10575             if(x !== undefined || y !== undefined){
10576                 a.points = {to: [
10577                     x !== undefined ? x : this.getX(),
10578                     y !== undefined ? y : this.getY()
10579                 ]};
10580             }
10581             if(op !== undefined){
10582                 a.opacity = {to: op};
10583             }
10584             if(o.xy !== undefined){
10585                 a.points = {to: o.xy};
10586             }
10587             arguments.callee.anim = this.fxanim(a,
10588                 o, 'motion', .35, "easeOut", function(){
10589                 el.afterFx(o);
10590             });
10591         });
10592         return this;
10593     },
10594
10595         /**
10596          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10597          * ending point of the effect.
10598          * Usage:
10599          *<pre><code>
10600 // default: slide the element downward while fading out
10601 el.ghost();
10602
10603 // custom: slide the element out to the right with a 2-second duration
10604 el.ghost('r', { duration: 2 });
10605
10606 // common config options shown with default values
10607 el.ghost('b', {
10608     easing: 'easeOut',
10609     duration: .5
10610     remove: false,
10611     useDisplay: false
10612 });
10613 </code></pre>
10614          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10615          * @param {Object} options (optional) Object literal with any of the Fx config options
10616          * @return {Roo.Element} The Element
10617          */
10618     ghost : function(anchor, o){
10619         var el = this.getFxEl();
10620         o = o || {};
10621
10622         el.queueFx(o, function(){
10623             anchor = anchor || "b";
10624
10625             // restore values after effect
10626             var r = this.getFxRestore();
10627             var w = this.getWidth(),
10628                 h = this.getHeight();
10629
10630             var st = this.dom.style;
10631
10632             var after = function(){
10633                 if(o.useDisplay){
10634                     el.setDisplayed(false);
10635                 }else{
10636                     el.hide();
10637                 }
10638
10639                 el.clearOpacity();
10640                 el.setPositioning(r.pos);
10641                 st.width = r.width;
10642                 st.height = r.height;
10643
10644                 el.afterFx(o);
10645             };
10646
10647             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10648             switch(anchor.toLowerCase()){
10649                 case "t":
10650                     pt.by = [0, -h];
10651                 break;
10652                 case "l":
10653                     pt.by = [-w, 0];
10654                 break;
10655                 case "r":
10656                     pt.by = [w, 0];
10657                 break;
10658                 case "b":
10659                     pt.by = [0, h];
10660                 break;
10661                 case "tl":
10662                     pt.by = [-w, -h];
10663                 break;
10664                 case "bl":
10665                     pt.by = [-w, h];
10666                 break;
10667                 case "br":
10668                     pt.by = [w, h];
10669                 break;
10670                 case "tr":
10671                     pt.by = [w, -h];
10672                 break;
10673             }
10674
10675             arguments.callee.anim = this.fxanim(a,
10676                 o,
10677                 'motion',
10678                 .5,
10679                 "easeOut", after);
10680         });
10681         return this;
10682     },
10683
10684         /**
10685          * Ensures that all effects queued after syncFx is called on the element are
10686          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10687          * @return {Roo.Element} The Element
10688          */
10689     syncFx : function(){
10690         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10691             block : false,
10692             concurrent : true,
10693             stopFx : false
10694         });
10695         return this;
10696     },
10697
10698         /**
10699          * Ensures that all effects queued after sequenceFx is called on the element are
10700          * run in sequence.  This is the opposite of {@link #syncFx}.
10701          * @return {Roo.Element} The Element
10702          */
10703     sequenceFx : function(){
10704         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10705             block : false,
10706             concurrent : false,
10707             stopFx : false
10708         });
10709         return this;
10710     },
10711
10712         /* @private */
10713     nextFx : function(){
10714         var ef = this.fxQueue[0];
10715         if(ef){
10716             ef.call(this);
10717         }
10718     },
10719
10720         /**
10721          * Returns true if the element has any effects actively running or queued, else returns false.
10722          * @return {Boolean} True if element has active effects, else false
10723          */
10724     hasActiveFx : function(){
10725         return this.fxQueue && this.fxQueue[0];
10726     },
10727
10728         /**
10729          * Stops any running effects and clears the element's internal effects queue if it contains
10730          * any additional effects that haven't started yet.
10731          * @return {Roo.Element} The Element
10732          */
10733     stopFx : function(){
10734         if(this.hasActiveFx()){
10735             var cur = this.fxQueue[0];
10736             if(cur && cur.anim && cur.anim.isAnimated()){
10737                 this.fxQueue = [cur]; // clear out others
10738                 cur.anim.stop(true);
10739             }
10740         }
10741         return this;
10742     },
10743
10744         /* @private */
10745     beforeFx : function(o){
10746         if(this.hasActiveFx() && !o.concurrent){
10747            if(o.stopFx){
10748                this.stopFx();
10749                return true;
10750            }
10751            return false;
10752         }
10753         return true;
10754     },
10755
10756         /**
10757          * Returns true if the element is currently blocking so that no other effect can be queued
10758          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10759          * used to ensure that an effect initiated by a user action runs to completion prior to the
10760          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10761          * @return {Boolean} True if blocking, else false
10762          */
10763     hasFxBlock : function(){
10764         var q = this.fxQueue;
10765         return q && q[0] && q[0].block;
10766     },
10767
10768         /* @private */
10769     queueFx : function(o, fn){
10770         if(!this.fxQueue){
10771             this.fxQueue = [];
10772         }
10773         if(!this.hasFxBlock()){
10774             Roo.applyIf(o, this.fxDefaults);
10775             if(!o.concurrent){
10776                 var run = this.beforeFx(o);
10777                 fn.block = o.block;
10778                 this.fxQueue.push(fn);
10779                 if(run){
10780                     this.nextFx();
10781                 }
10782             }else{
10783                 fn.call(this);
10784             }
10785         }
10786         return this;
10787     },
10788
10789         /* @private */
10790     fxWrap : function(pos, o, vis){
10791         var wrap;
10792         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10793             var wrapXY;
10794             if(o.fixPosition){
10795                 wrapXY = this.getXY();
10796             }
10797             var div = document.createElement("div");
10798             div.style.visibility = vis;
10799             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10800             wrap.setPositioning(pos);
10801             if(wrap.getStyle("position") == "static"){
10802                 wrap.position("relative");
10803             }
10804             this.clearPositioning('auto');
10805             wrap.clip();
10806             wrap.dom.appendChild(this.dom);
10807             if(wrapXY){
10808                 wrap.setXY(wrapXY);
10809             }
10810         }
10811         return wrap;
10812     },
10813
10814         /* @private */
10815     fxUnwrap : function(wrap, pos, o){
10816         this.clearPositioning();
10817         this.setPositioning(pos);
10818         if(!o.wrap){
10819             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10820             wrap.remove();
10821         }
10822     },
10823
10824         /* @private */
10825     getFxRestore : function(){
10826         var st = this.dom.style;
10827         return {pos: this.getPositioning(), width: st.width, height : st.height};
10828     },
10829
10830         /* @private */
10831     afterFx : function(o){
10832         if(o.afterStyle){
10833             this.applyStyles(o.afterStyle);
10834         }
10835         if(o.afterCls){
10836             this.addClass(o.afterCls);
10837         }
10838         if(o.remove === true){
10839             this.remove();
10840         }
10841         Roo.callback(o.callback, o.scope, [this]);
10842         if(!o.concurrent){
10843             this.fxQueue.shift();
10844             this.nextFx();
10845         }
10846     },
10847
10848         /* @private */
10849     getFxEl : function(){ // support for composite element fx
10850         return Roo.get(this.dom);
10851     },
10852
10853         /* @private */
10854     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10855         animType = animType || 'run';
10856         opt = opt || {};
10857         var anim = Roo.lib.Anim[animType](
10858             this.dom, args,
10859             (opt.duration || defaultDur) || .35,
10860             (opt.easing || defaultEase) || 'easeOut',
10861             function(){
10862                 Roo.callback(cb, this);
10863             },
10864             this
10865         );
10866         opt.anim = anim;
10867         return anim;
10868     }
10869 };
10870
10871 // backwords compat
10872 Roo.Fx.resize = Roo.Fx.scale;
10873
10874 //When included, Roo.Fx is automatically applied to Element so that all basic
10875 //effects are available directly via the Element API
10876 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10877  * Based on:
10878  * Ext JS Library 1.1.1
10879  * Copyright(c) 2006-2007, Ext JS, LLC.
10880  *
10881  * Originally Released Under LGPL - original licence link has changed is not relivant.
10882  *
10883  * Fork - LGPL
10884  * <script type="text/javascript">
10885  */
10886
10887
10888 /**
10889  * @class Roo.CompositeElement
10890  * Standard composite class. Creates a Roo.Element for every element in the collection.
10891  * <br><br>
10892  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10893  * actions will be performed on all the elements in this collection.</b>
10894  * <br><br>
10895  * All methods return <i>this</i> and can be chained.
10896  <pre><code>
10897  var els = Roo.select("#some-el div.some-class", true);
10898  // or select directly from an existing element
10899  var el = Roo.get('some-el');
10900  el.select('div.some-class', true);
10901
10902  els.setWidth(100); // all elements become 100 width
10903  els.hide(true); // all elements fade out and hide
10904  // or
10905  els.setWidth(100).hide(true);
10906  </code></pre>
10907  */
10908 Roo.CompositeElement = function(els){
10909     this.elements = [];
10910     this.addElements(els);
10911 };
10912 Roo.CompositeElement.prototype = {
10913     isComposite: true,
10914     addElements : function(els){
10915         if(!els) return this;
10916         if(typeof els == "string"){
10917             els = Roo.Element.selectorFunction(els);
10918         }
10919         var yels = this.elements;
10920         var index = yels.length-1;
10921         for(var i = 0, len = els.length; i < len; i++) {
10922                 yels[++index] = Roo.get(els[i]);
10923         }
10924         return this;
10925     },
10926
10927     /**
10928     * Clears this composite and adds the elements returned by the passed selector.
10929     * @param {String/Array} els A string CSS selector, an array of elements or an element
10930     * @return {CompositeElement} this
10931     */
10932     fill : function(els){
10933         this.elements = [];
10934         this.add(els);
10935         return this;
10936     },
10937
10938     /**
10939     * Filters this composite to only elements that match the passed selector.
10940     * @param {String} selector A string CSS selector
10941     * @return {CompositeElement} this
10942     */
10943     filter : function(selector){
10944         var els = [];
10945         this.each(function(el){
10946             if(el.is(selector)){
10947                 els[els.length] = el.dom;
10948             }
10949         });
10950         this.fill(els);
10951         return this;
10952     },
10953
10954     invoke : function(fn, args){
10955         var els = this.elements;
10956         for(var i = 0, len = els.length; i < len; i++) {
10957                 Roo.Element.prototype[fn].apply(els[i], args);
10958         }
10959         return this;
10960     },
10961     /**
10962     * Adds elements to this composite.
10963     * @param {String/Array} els A string CSS selector, an array of elements or an element
10964     * @return {CompositeElement} this
10965     */
10966     add : function(els){
10967         if(typeof els == "string"){
10968             this.addElements(Roo.Element.selectorFunction(els));
10969         }else if(els.length !== undefined){
10970             this.addElements(els);
10971         }else{
10972             this.addElements([els]);
10973         }
10974         return this;
10975     },
10976     /**
10977     * Calls the passed function passing (el, this, index) for each element in this composite.
10978     * @param {Function} fn The function to call
10979     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10980     * @return {CompositeElement} this
10981     */
10982     each : function(fn, scope){
10983         var els = this.elements;
10984         for(var i = 0, len = els.length; i < len; i++){
10985             if(fn.call(scope || els[i], els[i], this, i) === false) {
10986                 break;
10987             }
10988         }
10989         return this;
10990     },
10991
10992     /**
10993      * Returns the Element object at the specified index
10994      * @param {Number} index
10995      * @return {Roo.Element}
10996      */
10997     item : function(index){
10998         return this.elements[index] || null;
10999     },
11000
11001     /**
11002      * Returns the first Element
11003      * @return {Roo.Element}
11004      */
11005     first : function(){
11006         return this.item(0);
11007     },
11008
11009     /**
11010      * Returns the last Element
11011      * @return {Roo.Element}
11012      */
11013     last : function(){
11014         return this.item(this.elements.length-1);
11015     },
11016
11017     /**
11018      * Returns the number of elements in this composite
11019      * @return Number
11020      */
11021     getCount : function(){
11022         return this.elements.length;
11023     },
11024
11025     /**
11026      * Returns true if this composite contains the passed element
11027      * @return Boolean
11028      */
11029     contains : function(el){
11030         return this.indexOf(el) !== -1;
11031     },
11032
11033     /**
11034      * Returns true if this composite contains the passed element
11035      * @return Boolean
11036      */
11037     indexOf : function(el){
11038         return this.elements.indexOf(Roo.get(el));
11039     },
11040
11041
11042     /**
11043     * Removes the specified element(s).
11044     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11045     * or an array of any of those.
11046     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11047     * @return {CompositeElement} this
11048     */
11049     removeElement : function(el, removeDom){
11050         if(el instanceof Array){
11051             for(var i = 0, len = el.length; i < len; i++){
11052                 this.removeElement(el[i]);
11053             }
11054             return this;
11055         }
11056         var index = typeof el == 'number' ? el : this.indexOf(el);
11057         if(index !== -1){
11058             if(removeDom){
11059                 var d = this.elements[index];
11060                 if(d.dom){
11061                     d.remove();
11062                 }else{
11063                     d.parentNode.removeChild(d);
11064                 }
11065             }
11066             this.elements.splice(index, 1);
11067         }
11068         return this;
11069     },
11070
11071     /**
11072     * Replaces the specified element with the passed element.
11073     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11074     * to replace.
11075     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11076     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11077     * @return {CompositeElement} this
11078     */
11079     replaceElement : function(el, replacement, domReplace){
11080         var index = typeof el == 'number' ? el : this.indexOf(el);
11081         if(index !== -1){
11082             if(domReplace){
11083                 this.elements[index].replaceWith(replacement);
11084             }else{
11085                 this.elements.splice(index, 1, Roo.get(replacement))
11086             }
11087         }
11088         return this;
11089     },
11090
11091     /**
11092      * Removes all elements.
11093      */
11094     clear : function(){
11095         this.elements = [];
11096     }
11097 };
11098 (function(){
11099     Roo.CompositeElement.createCall = function(proto, fnName){
11100         if(!proto[fnName]){
11101             proto[fnName] = function(){
11102                 return this.invoke(fnName, arguments);
11103             };
11104         }
11105     };
11106     for(var fnName in Roo.Element.prototype){
11107         if(typeof Roo.Element.prototype[fnName] == "function"){
11108             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11109         }
11110     };
11111 })();
11112 /*
11113  * Based on:
11114  * Ext JS Library 1.1.1
11115  * Copyright(c) 2006-2007, Ext JS, LLC.
11116  *
11117  * Originally Released Under LGPL - original licence link has changed is not relivant.
11118  *
11119  * Fork - LGPL
11120  * <script type="text/javascript">
11121  */
11122
11123 /**
11124  * @class Roo.CompositeElementLite
11125  * @extends Roo.CompositeElement
11126  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11127  <pre><code>
11128  var els = Roo.select("#some-el div.some-class");
11129  // or select directly from an existing element
11130  var el = Roo.get('some-el');
11131  el.select('div.some-class');
11132
11133  els.setWidth(100); // all elements become 100 width
11134  els.hide(true); // all elements fade out and hide
11135  // or
11136  els.setWidth(100).hide(true);
11137  </code></pre><br><br>
11138  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11139  * actions will be performed on all the elements in this collection.</b>
11140  */
11141 Roo.CompositeElementLite = function(els){
11142     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11143     this.el = new Roo.Element.Flyweight();
11144 };
11145 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11146     addElements : function(els){
11147         if(els){
11148             if(els instanceof Array){
11149                 this.elements = this.elements.concat(els);
11150             }else{
11151                 var yels = this.elements;
11152                 var index = yels.length-1;
11153                 for(var i = 0, len = els.length; i < len; i++) {
11154                     yels[++index] = els[i];
11155                 }
11156             }
11157         }
11158         return this;
11159     },
11160     invoke : function(fn, args){
11161         var els = this.elements;
11162         var el = this.el;
11163         for(var i = 0, len = els.length; i < len; i++) {
11164             el.dom = els[i];
11165                 Roo.Element.prototype[fn].apply(el, args);
11166         }
11167         return this;
11168     },
11169     /**
11170      * Returns a flyweight Element of the dom element object at the specified index
11171      * @param {Number} index
11172      * @return {Roo.Element}
11173      */
11174     item : function(index){
11175         if(!this.elements[index]){
11176             return null;
11177         }
11178         this.el.dom = this.elements[index];
11179         return this.el;
11180     },
11181
11182     // fixes scope with flyweight
11183     addListener : function(eventName, handler, scope, opt){
11184         var els = this.elements;
11185         for(var i = 0, len = els.length; i < len; i++) {
11186             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11187         }
11188         return this;
11189     },
11190
11191     /**
11192     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11193     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11194     * a reference to the dom node, use el.dom.</b>
11195     * @param {Function} fn The function to call
11196     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11197     * @return {CompositeElement} this
11198     */
11199     each : function(fn, scope){
11200         var els = this.elements;
11201         var el = this.el;
11202         for(var i = 0, len = els.length; i < len; i++){
11203             el.dom = els[i];
11204                 if(fn.call(scope || el, el, this, i) === false){
11205                 break;
11206             }
11207         }
11208         return this;
11209     },
11210
11211     indexOf : function(el){
11212         return this.elements.indexOf(Roo.getDom(el));
11213     },
11214
11215     replaceElement : function(el, replacement, domReplace){
11216         var index = typeof el == 'number' ? el : this.indexOf(el);
11217         if(index !== -1){
11218             replacement = Roo.getDom(replacement);
11219             if(domReplace){
11220                 var d = this.elements[index];
11221                 d.parentNode.insertBefore(replacement, d);
11222                 d.parentNode.removeChild(d);
11223             }
11224             this.elements.splice(index, 1, replacement);
11225         }
11226         return this;
11227     }
11228 });
11229 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11230
11231 /*
11232  * Based on:
11233  * Ext JS Library 1.1.1
11234  * Copyright(c) 2006-2007, Ext JS, LLC.
11235  *
11236  * Originally Released Under LGPL - original licence link has changed is not relivant.
11237  *
11238  * Fork - LGPL
11239  * <script type="text/javascript">
11240  */
11241
11242  
11243
11244 /**
11245  * @class Roo.data.Connection
11246  * @extends Roo.util.Observable
11247  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11248  * either to a configured URL, or to a URL specified at request time.<br><br>
11249  * <p>
11250  * Requests made by this class are asynchronous, and will return immediately. No data from
11251  * the server will be available to the statement immediately following the {@link #request} call.
11252  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11253  * <p>
11254  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11255  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11256  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11257  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11258  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11259  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11260  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11261  * standard DOM methods.
11262  * @constructor
11263  * @param {Object} config a configuration object.
11264  */
11265 Roo.data.Connection = function(config){
11266     Roo.apply(this, config);
11267     this.addEvents({
11268         /**
11269          * @event beforerequest
11270          * Fires before a network request is made to retrieve a data object.
11271          * @param {Connection} conn This Connection object.
11272          * @param {Object} options The options config object passed to the {@link #request} method.
11273          */
11274         "beforerequest" : true,
11275         /**
11276          * @event requestcomplete
11277          * Fires if the request was successfully completed.
11278          * @param {Connection} conn This Connection object.
11279          * @param {Object} response The XHR object containing the response data.
11280          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11281          * @param {Object} options The options config object passed to the {@link #request} method.
11282          */
11283         "requestcomplete" : true,
11284         /**
11285          * @event requestexception
11286          * Fires if an error HTTP status was returned from the server.
11287          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11288          * @param {Connection} conn This Connection object.
11289          * @param {Object} response The XHR object containing the response data.
11290          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11291          * @param {Object} options The options config object passed to the {@link #request} method.
11292          */
11293         "requestexception" : true
11294     });
11295     Roo.data.Connection.superclass.constructor.call(this);
11296 };
11297
11298 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11299     /**
11300      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11301      */
11302     /**
11303      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11304      * extra parameters to each request made by this object. (defaults to undefined)
11305      */
11306     /**
11307      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11308      *  to each request made by this object. (defaults to undefined)
11309      */
11310     /**
11311      * @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)
11312      */
11313     /**
11314      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11315      */
11316     timeout : 30000,
11317     /**
11318      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11319      * @type Boolean
11320      */
11321     autoAbort:false,
11322
11323     /**
11324      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11325      * @type Boolean
11326      */
11327     disableCaching: true,
11328
11329     /**
11330      * Sends an HTTP request to a remote server.
11331      * @param {Object} options An object which may contain the following properties:<ul>
11332      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11333      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11334      * request, a url encoded string or a function to call to get either.</li>
11335      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11336      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11337      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11338      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11339      * <li>options {Object} The parameter to the request call.</li>
11340      * <li>success {Boolean} True if the request succeeded.</li>
11341      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11342      * </ul></li>
11343      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11344      * The callback is passed the following parameters:<ul>
11345      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11346      * <li>options {Object} The parameter to the request call.</li>
11347      * </ul></li>
11348      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11349      * The callback is passed the following parameters:<ul>
11350      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11351      * <li>options {Object} The parameter to the request call.</li>
11352      * </ul></li>
11353      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11354      * for the callback function. Defaults to the browser window.</li>
11355      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11356      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11357      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11358      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11359      * params for the post data. Any params will be appended to the URL.</li>
11360      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11361      * </ul>
11362      * @return {Number} transactionId
11363      */
11364     request : function(o){
11365         if(this.fireEvent("beforerequest", this, o) !== false){
11366             var p = o.params;
11367
11368             if(typeof p == "function"){
11369                 p = p.call(o.scope||window, o);
11370             }
11371             if(typeof p == "object"){
11372                 p = Roo.urlEncode(o.params);
11373             }
11374             if(this.extraParams){
11375                 var extras = Roo.urlEncode(this.extraParams);
11376                 p = p ? (p + '&' + extras) : extras;
11377             }
11378
11379             var url = o.url || this.url;
11380             if(typeof url == 'function'){
11381                 url = url.call(o.scope||window, o);
11382             }
11383
11384             if(o.form){
11385                 var form = Roo.getDom(o.form);
11386                 url = url || form.action;
11387
11388                 var enctype = form.getAttribute("enctype");
11389                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11390                     return this.doFormUpload(o, p, url);
11391                 }
11392                 var f = Roo.lib.Ajax.serializeForm(form);
11393                 p = p ? (p + '&' + f) : f;
11394             }
11395
11396             var hs = o.headers;
11397             if(this.defaultHeaders){
11398                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11399                 if(!o.headers){
11400                     o.headers = hs;
11401                 }
11402             }
11403
11404             var cb = {
11405                 success: this.handleResponse,
11406                 failure: this.handleFailure,
11407                 scope: this,
11408                 argument: {options: o},
11409                 timeout : o.timeout || this.timeout
11410             };
11411
11412             var method = o.method||this.method||(p ? "POST" : "GET");
11413
11414             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11415                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11416             }
11417
11418             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11419                 if(o.autoAbort){
11420                     this.abort();
11421                 }
11422             }else if(this.autoAbort !== false){
11423                 this.abort();
11424             }
11425
11426             if((method == 'GET' && p) || o.xmlData){
11427                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11428                 p = '';
11429             }
11430             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11431             return this.transId;
11432         }else{
11433             Roo.callback(o.callback, o.scope, [o, null, null]);
11434             return null;
11435         }
11436     },
11437
11438     /**
11439      * Determine whether this object has a request outstanding.
11440      * @param {Number} transactionId (Optional) defaults to the last transaction
11441      * @return {Boolean} True if there is an outstanding request.
11442      */
11443     isLoading : function(transId){
11444         if(transId){
11445             return Roo.lib.Ajax.isCallInProgress(transId);
11446         }else{
11447             return this.transId ? true : false;
11448         }
11449     },
11450
11451     /**
11452      * Aborts any outstanding request.
11453      * @param {Number} transactionId (Optional) defaults to the last transaction
11454      */
11455     abort : function(transId){
11456         if(transId || this.isLoading()){
11457             Roo.lib.Ajax.abort(transId || this.transId);
11458         }
11459     },
11460
11461     // private
11462     handleResponse : function(response){
11463         this.transId = false;
11464         var options = response.argument.options;
11465         response.argument = options ? options.argument : null;
11466         this.fireEvent("requestcomplete", this, response, options);
11467         Roo.callback(options.success, options.scope, [response, options]);
11468         Roo.callback(options.callback, options.scope, [options, true, response]);
11469     },
11470
11471     // private
11472     handleFailure : function(response, e){
11473         this.transId = false;
11474         var options = response.argument.options;
11475         response.argument = options ? options.argument : null;
11476         this.fireEvent("requestexception", this, response, options, e);
11477         Roo.callback(options.failure, options.scope, [response, options]);
11478         Roo.callback(options.callback, options.scope, [options, false, response]);
11479     },
11480
11481     // private
11482     doFormUpload : function(o, ps, url){
11483         var id = Roo.id();
11484         var frame = document.createElement('iframe');
11485         frame.id = id;
11486         frame.name = id;
11487         frame.className = 'x-hidden';
11488         if(Roo.isIE){
11489             frame.src = Roo.SSL_SECURE_URL;
11490         }
11491         document.body.appendChild(frame);
11492
11493         if(Roo.isIE){
11494            document.frames[id].name = id;
11495         }
11496
11497         var form = Roo.getDom(o.form);
11498         form.target = id;
11499         form.method = 'POST';
11500         form.enctype = form.encoding = 'multipart/form-data';
11501         if(url){
11502             form.action = url;
11503         }
11504
11505         var hiddens, hd;
11506         if(ps){ // add dynamic params
11507             hiddens = [];
11508             ps = Roo.urlDecode(ps, false);
11509             for(var k in ps){
11510                 if(ps.hasOwnProperty(k)){
11511                     hd = document.createElement('input');
11512                     hd.type = 'hidden';
11513                     hd.name = k;
11514                     hd.value = ps[k];
11515                     form.appendChild(hd);
11516                     hiddens.push(hd);
11517                 }
11518             }
11519         }
11520
11521         function cb(){
11522             var r = {  // bogus response object
11523                 responseText : '',
11524                 responseXML : null
11525             };
11526
11527             r.argument = o ? o.argument : null;
11528
11529             try { //
11530                 var doc;
11531                 if(Roo.isIE){
11532                     doc = frame.contentWindow.document;
11533                 }else {
11534                     doc = (frame.contentDocument || window.frames[id].document);
11535                 }
11536                 if(doc && doc.body){
11537                     r.responseText = doc.body.innerHTML;
11538                 }
11539                 if(doc && doc.XMLDocument){
11540                     r.responseXML = doc.XMLDocument;
11541                 }else {
11542                     r.responseXML = doc;
11543                 }
11544             }
11545             catch(e) {
11546                 // ignore
11547             }
11548
11549             Roo.EventManager.removeListener(frame, 'load', cb, this);
11550
11551             this.fireEvent("requestcomplete", this, r, o);
11552             Roo.callback(o.success, o.scope, [r, o]);
11553             Roo.callback(o.callback, o.scope, [o, true, r]);
11554
11555             setTimeout(function(){document.body.removeChild(frame);}, 100);
11556         }
11557
11558         Roo.EventManager.on(frame, 'load', cb, this);
11559         form.submit();
11560
11561         if(hiddens){ // remove dynamic params
11562             for(var i = 0, len = hiddens.length; i < len; i++){
11563                 form.removeChild(hiddens[i]);
11564             }
11565         }
11566     }
11567 });
11568 /*
11569  * Based on:
11570  * Ext JS Library 1.1.1
11571  * Copyright(c) 2006-2007, Ext JS, LLC.
11572  *
11573  * Originally Released Under LGPL - original licence link has changed is not relivant.
11574  *
11575  * Fork - LGPL
11576  * <script type="text/javascript">
11577  */
11578  
11579 /**
11580  * Global Ajax request class.
11581  * 
11582  * @class Roo.Ajax
11583  * @extends Roo.data.Connection
11584  * @static
11585  * 
11586  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11587  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11588  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11589  * @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)
11590  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11591  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11592  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11593  */
11594 Roo.Ajax = new Roo.data.Connection({
11595     // fix up the docs
11596     /**
11597      * @scope Roo.Ajax
11598      * @type {Boolear} 
11599      */
11600     autoAbort : false,
11601
11602     /**
11603      * Serialize the passed form into a url encoded string
11604      * @scope Roo.Ajax
11605      * @param {String/HTMLElement} form
11606      * @return {String}
11607      */
11608     serializeForm : function(form){
11609         return Roo.lib.Ajax.serializeForm(form);
11610     }
11611 });/*
11612  * Based on:
11613  * Ext JS Library 1.1.1
11614  * Copyright(c) 2006-2007, Ext JS, LLC.
11615  *
11616  * Originally Released Under LGPL - original licence link has changed is not relivant.
11617  *
11618  * Fork - LGPL
11619  * <script type="text/javascript">
11620  */
11621
11622  
11623 /**
11624  * @class Roo.UpdateManager
11625  * @extends Roo.util.Observable
11626  * Provides AJAX-style update for Element object.<br><br>
11627  * Usage:<br>
11628  * <pre><code>
11629  * // Get it from a Roo.Element object
11630  * var el = Roo.get("foo");
11631  * var mgr = el.getUpdateManager();
11632  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11633  * ...
11634  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11635  * <br>
11636  * // or directly (returns the same UpdateManager instance)
11637  * var mgr = new Roo.UpdateManager("myElementId");
11638  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11639  * mgr.on("update", myFcnNeedsToKnow);
11640  * <br>
11641    // short handed call directly from the element object
11642    Roo.get("foo").load({
11643         url: "bar.php",
11644         scripts:true,
11645         params: "for=bar",
11646         text: "Loading Foo..."
11647    });
11648  * </code></pre>
11649  * @constructor
11650  * Create new UpdateManager directly.
11651  * @param {String/HTMLElement/Roo.Element} el The element to update
11652  * @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).
11653  */
11654 Roo.UpdateManager = function(el, forceNew){
11655     el = Roo.get(el);
11656     if(!forceNew && el.updateManager){
11657         return el.updateManager;
11658     }
11659     /**
11660      * The Element object
11661      * @type Roo.Element
11662      */
11663     this.el = el;
11664     /**
11665      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11666      * @type String
11667      */
11668     this.defaultUrl = null;
11669
11670     this.addEvents({
11671         /**
11672          * @event beforeupdate
11673          * Fired before an update is made, return false from your handler and the update is cancelled.
11674          * @param {Roo.Element} el
11675          * @param {String/Object/Function} url
11676          * @param {String/Object} params
11677          */
11678         "beforeupdate": true,
11679         /**
11680          * @event update
11681          * Fired after successful update is made.
11682          * @param {Roo.Element} el
11683          * @param {Object} oResponseObject The response Object
11684          */
11685         "update": true,
11686         /**
11687          * @event failure
11688          * Fired on update failure.
11689          * @param {Roo.Element} el
11690          * @param {Object} oResponseObject The response Object
11691          */
11692         "failure": true
11693     });
11694     var d = Roo.UpdateManager.defaults;
11695     /**
11696      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11697      * @type String
11698      */
11699     this.sslBlankUrl = d.sslBlankUrl;
11700     /**
11701      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11702      * @type Boolean
11703      */
11704     this.disableCaching = d.disableCaching;
11705     /**
11706      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11707      * @type String
11708      */
11709     this.indicatorText = d.indicatorText;
11710     /**
11711      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11712      * @type String
11713      */
11714     this.showLoadIndicator = d.showLoadIndicator;
11715     /**
11716      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11717      * @type Number
11718      */
11719     this.timeout = d.timeout;
11720
11721     /**
11722      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11723      * @type Boolean
11724      */
11725     this.loadScripts = d.loadScripts;
11726
11727     /**
11728      * Transaction object of current executing transaction
11729      */
11730     this.transaction = null;
11731
11732     /**
11733      * @private
11734      */
11735     this.autoRefreshProcId = null;
11736     /**
11737      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11738      * @type Function
11739      */
11740     this.refreshDelegate = this.refresh.createDelegate(this);
11741     /**
11742      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11743      * @type Function
11744      */
11745     this.updateDelegate = this.update.createDelegate(this);
11746     /**
11747      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11748      * @type Function
11749      */
11750     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11751     /**
11752      * @private
11753      */
11754     this.successDelegate = this.processSuccess.createDelegate(this);
11755     /**
11756      * @private
11757      */
11758     this.failureDelegate = this.processFailure.createDelegate(this);
11759
11760     if(!this.renderer){
11761      /**
11762       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11763       */
11764     this.renderer = new Roo.UpdateManager.BasicRenderer();
11765     }
11766     
11767     Roo.UpdateManager.superclass.constructor.call(this);
11768 };
11769
11770 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11771     /**
11772      * Get the Element this UpdateManager is bound to
11773      * @return {Roo.Element} The element
11774      */
11775     getEl : function(){
11776         return this.el;
11777     },
11778     /**
11779      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11780      * @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:
11781 <pre><code>
11782 um.update({<br/>
11783     url: "your-url.php",<br/>
11784     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11785     callback: yourFunction,<br/>
11786     scope: yourObject, //(optional scope)  <br/>
11787     discardUrl: false, <br/>
11788     nocache: false,<br/>
11789     text: "Loading...",<br/>
11790     timeout: 30,<br/>
11791     scripts: false<br/>
11792 });
11793 </code></pre>
11794      * The only required property is url. The optional properties nocache, text and scripts
11795      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11796      * @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}
11797      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11798      * @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.
11799      */
11800     update : function(url, params, callback, discardUrl){
11801         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11802             var method = this.method,
11803                 cfg;
11804             if(typeof url == "object"){ // must be config object
11805                 cfg = url;
11806                 url = cfg.url;
11807                 params = params || cfg.params;
11808                 callback = callback || cfg.callback;
11809                 discardUrl = discardUrl || cfg.discardUrl;
11810                 if(callback && cfg.scope){
11811                     callback = callback.createDelegate(cfg.scope);
11812                 }
11813                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11814                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11815                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11816                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11817                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11818             }
11819             this.showLoading();
11820             if(!discardUrl){
11821                 this.defaultUrl = url;
11822             }
11823             if(typeof url == "function"){
11824                 url = url.call(this);
11825             }
11826
11827             method = method || (params ? "POST" : "GET");
11828             if(method == "GET"){
11829                 url = this.prepareUrl(url);
11830             }
11831
11832             var o = Roo.apply(cfg ||{}, {
11833                 url : url,
11834                 params: params,
11835                 success: this.successDelegate,
11836                 failure: this.failureDelegate,
11837                 callback: undefined,
11838                 timeout: (this.timeout*1000),
11839                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11840             });
11841             Roo.log("updated manager called with timeout of " + o.timeout);
11842             this.transaction = Roo.Ajax.request(o);
11843         }
11844     },
11845
11846     /**
11847      * 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.
11848      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11849      * @param {String/HTMLElement} form The form Id or form element
11850      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11851      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11852      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11853      */
11854     formUpdate : function(form, url, reset, callback){
11855         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11856             if(typeof url == "function"){
11857                 url = url.call(this);
11858             }
11859             form = Roo.getDom(form);
11860             this.transaction = Roo.Ajax.request({
11861                 form: form,
11862                 url:url,
11863                 success: this.successDelegate,
11864                 failure: this.failureDelegate,
11865                 timeout: (this.timeout*1000),
11866                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11867             });
11868             this.showLoading.defer(1, this);
11869         }
11870     },
11871
11872     /**
11873      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11874      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11875      */
11876     refresh : function(callback){
11877         if(this.defaultUrl == null){
11878             return;
11879         }
11880         this.update(this.defaultUrl, null, callback, true);
11881     },
11882
11883     /**
11884      * Set this element to auto refresh.
11885      * @param {Number} interval How often to update (in seconds).
11886      * @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)
11887      * @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}
11888      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11889      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11890      */
11891     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11892         if(refreshNow){
11893             this.update(url || this.defaultUrl, params, callback, true);
11894         }
11895         if(this.autoRefreshProcId){
11896             clearInterval(this.autoRefreshProcId);
11897         }
11898         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11899     },
11900
11901     /**
11902      * Stop auto refresh on this element.
11903      */
11904      stopAutoRefresh : function(){
11905         if(this.autoRefreshProcId){
11906             clearInterval(this.autoRefreshProcId);
11907             delete this.autoRefreshProcId;
11908         }
11909     },
11910
11911     isAutoRefreshing : function(){
11912        return this.autoRefreshProcId ? true : false;
11913     },
11914     /**
11915      * Called to update the element to "Loading" state. Override to perform custom action.
11916      */
11917     showLoading : function(){
11918         if(this.showLoadIndicator){
11919             this.el.update(this.indicatorText);
11920         }
11921     },
11922
11923     /**
11924      * Adds unique parameter to query string if disableCaching = true
11925      * @private
11926      */
11927     prepareUrl : function(url){
11928         if(this.disableCaching){
11929             var append = "_dc=" + (new Date().getTime());
11930             if(url.indexOf("?") !== -1){
11931                 url += "&" + append;
11932             }else{
11933                 url += "?" + append;
11934             }
11935         }
11936         return url;
11937     },
11938
11939     /**
11940      * @private
11941      */
11942     processSuccess : function(response){
11943         this.transaction = null;
11944         if(response.argument.form && response.argument.reset){
11945             try{ // put in try/catch since some older FF releases had problems with this
11946                 response.argument.form.reset();
11947             }catch(e){}
11948         }
11949         if(this.loadScripts){
11950             this.renderer.render(this.el, response, this,
11951                 this.updateComplete.createDelegate(this, [response]));
11952         }else{
11953             this.renderer.render(this.el, response, this);
11954             this.updateComplete(response);
11955         }
11956     },
11957
11958     updateComplete : function(response){
11959         this.fireEvent("update", this.el, response);
11960         if(typeof response.argument.callback == "function"){
11961             response.argument.callback(this.el, true, response);
11962         }
11963     },
11964
11965     /**
11966      * @private
11967      */
11968     processFailure : function(response){
11969         this.transaction = null;
11970         this.fireEvent("failure", this.el, response);
11971         if(typeof response.argument.callback == "function"){
11972             response.argument.callback(this.el, false, response);
11973         }
11974     },
11975
11976     /**
11977      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11978      * @param {Object} renderer The object implementing the render() method
11979      */
11980     setRenderer : function(renderer){
11981         this.renderer = renderer;
11982     },
11983
11984     getRenderer : function(){
11985        return this.renderer;
11986     },
11987
11988     /**
11989      * Set the defaultUrl used for updates
11990      * @param {String/Function} defaultUrl The url or a function to call to get the url
11991      */
11992     setDefaultUrl : function(defaultUrl){
11993         this.defaultUrl = defaultUrl;
11994     },
11995
11996     /**
11997      * Aborts the executing transaction
11998      */
11999     abort : function(){
12000         if(this.transaction){
12001             Roo.Ajax.abort(this.transaction);
12002         }
12003     },
12004
12005     /**
12006      * Returns true if an update is in progress
12007      * @return {Boolean}
12008      */
12009     isUpdating : function(){
12010         if(this.transaction){
12011             return Roo.Ajax.isLoading(this.transaction);
12012         }
12013         return false;
12014     }
12015 });
12016
12017 /**
12018  * @class Roo.UpdateManager.defaults
12019  * @static (not really - but it helps the doc tool)
12020  * The defaults collection enables customizing the default properties of UpdateManager
12021  */
12022    Roo.UpdateManager.defaults = {
12023        /**
12024          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12025          * @type Number
12026          */
12027          timeout : 30,
12028
12029          /**
12030          * True to process scripts by default (Defaults to false).
12031          * @type Boolean
12032          */
12033         loadScripts : false,
12034
12035         /**
12036         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12037         * @type String
12038         */
12039         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12040         /**
12041          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12042          * @type Boolean
12043          */
12044         disableCaching : false,
12045         /**
12046          * Whether to show indicatorText when loading (Defaults to true).
12047          * @type Boolean
12048          */
12049         showLoadIndicator : true,
12050         /**
12051          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12052          * @type String
12053          */
12054         indicatorText : '<div class="loading-indicator">Loading...</div>'
12055    };
12056
12057 /**
12058  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12059  *Usage:
12060  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12061  * @param {String/HTMLElement/Roo.Element} el The element to update
12062  * @param {String} url The url
12063  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12064  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12065  * @static
12066  * @deprecated
12067  * @member Roo.UpdateManager
12068  */
12069 Roo.UpdateManager.updateElement = function(el, url, params, options){
12070     var um = Roo.get(el, true).getUpdateManager();
12071     Roo.apply(um, options);
12072     um.update(url, params, options ? options.callback : null);
12073 };
12074 // alias for backwards compat
12075 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12076 /**
12077  * @class Roo.UpdateManager.BasicRenderer
12078  * Default Content renderer. Updates the elements innerHTML with the responseText.
12079  */
12080 Roo.UpdateManager.BasicRenderer = function(){};
12081
12082 Roo.UpdateManager.BasicRenderer.prototype = {
12083     /**
12084      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12085      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12086      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12087      * @param {Roo.Element} el The element being rendered
12088      * @param {Object} response The YUI Connect response object
12089      * @param {UpdateManager} updateManager The calling update manager
12090      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12091      */
12092      render : function(el, response, updateManager, callback){
12093         el.update(response.responseText, updateManager.loadScripts, callback);
12094     }
12095 };
12096 /*
12097  * Based on:
12098  * Roo JS
12099  * (c)) Alan Knowles
12100  * Licence : LGPL
12101  */
12102
12103
12104 /**
12105  * @class Roo.DomTemplate
12106  * @extends Roo.Template
12107  * An effort at a dom based template engine..
12108  *
12109  * Similar to XTemplate, except it uses dom parsing to create the template..
12110  *
12111  * Supported features:
12112  *
12113  *  Tags:
12114
12115 <pre><code>
12116       {a_variable} - output encoded.
12117       {a_variable.format:("Y-m-d")} - call a method on the variable
12118       {a_variable:raw} - unencoded output
12119       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12120       {a_variable:this.method_on_template(...)} - call a method on the template object.
12121  
12122 </code></pre>
12123  *  The tpl tag:
12124 <pre><code>
12125         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12126         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12127         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12128         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12129   
12130 </code></pre>
12131  *      
12132  */
12133 Roo.DomTemplate = function()
12134 {
12135      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12136      if (this.html) {
12137         this.compile();
12138      }
12139 };
12140
12141
12142 Roo.extend(Roo.DomTemplate, Roo.Template, {
12143     /**
12144      * id counter for sub templates.
12145      */
12146     id : 0,
12147     /**
12148      * flag to indicate if dom parser is inside a pre,
12149      * it will strip whitespace if not.
12150      */
12151     inPre : false,
12152     
12153     /**
12154      * The various sub templates
12155      */
12156     tpls : false,
12157     
12158     
12159     
12160     /**
12161      *
12162      * basic tag replacing syntax
12163      * WORD:WORD()
12164      *
12165      * // you can fake an object call by doing this
12166      *  x.t:(test,tesT) 
12167      * 
12168      */
12169     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12170     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12171     
12172     iterChild : function (node, method) {
12173         
12174         var oldPre = this.inPre;
12175         if (node.tagName == 'PRE') {
12176             this.inPre = true;
12177         }
12178         for( var i = 0; i < node.childNodes.length; i++) {
12179             method.call(this, node.childNodes[i]);
12180         }
12181         this.inPre = oldPre;
12182     },
12183     
12184     
12185     
12186     /**
12187      * compile the template
12188      *
12189      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12190      *
12191      */
12192     compile: function()
12193     {
12194         var s = this.html;
12195         
12196         // covert the html into DOM...
12197         var doc = false;
12198         var div =false;
12199         try {
12200             doc = document.implementation.createHTMLDocument("");
12201             doc.documentElement.innerHTML =   this.html  ;
12202             div = doc.documentElement;
12203         } catch (e) {
12204             // old IE... - nasty -- it causes all sorts of issues.. with
12205             // images getting pulled from server..
12206             div = document.createElement('div');
12207             div.innerHTML = this.html;
12208         }
12209         //doc.documentElement.innerHTML = htmlBody
12210          
12211         
12212         
12213         this.tpls = [];
12214         var _t = this;
12215         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12216         
12217         var tpls = this.tpls;
12218         
12219         // create a top level template from the snippet..
12220         
12221         //Roo.log(div.innerHTML);
12222         
12223         var tpl = {
12224             uid : 'master',
12225             id : this.id++,
12226             attr : false,
12227             value : false,
12228             body : div.innerHTML,
12229             
12230             forCall : false,
12231             execCall : false,
12232             dom : div,
12233             isTop : true
12234             
12235         };
12236         tpls.unshift(tpl);
12237         
12238         
12239         // compile them...
12240         this.tpls = [];
12241         Roo.each(tpls, function(tp){
12242             this.compileTpl(tp);
12243             this.tpls[tp.id] = tp;
12244         }, this);
12245         
12246         this.master = tpls[0];
12247         return this;
12248         
12249         
12250     },
12251     
12252     compileNode : function(node, istop) {
12253         // test for
12254         //Roo.log(node);
12255         
12256         
12257         // skip anything not a tag..
12258         if (node.nodeType != 1) {
12259             if (node.nodeType == 3 && !this.inPre) {
12260                 // reduce white space..
12261                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12262                 
12263             }
12264             return;
12265         }
12266         
12267         var tpl = {
12268             uid : false,
12269             id : false,
12270             attr : false,
12271             value : false,
12272             body : '',
12273             
12274             forCall : false,
12275             execCall : false,
12276             dom : false,
12277             isTop : istop
12278             
12279             
12280         };
12281         
12282         
12283         switch(true) {
12284             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12285             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12286             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12287             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12288             // no default..
12289         }
12290         
12291         
12292         if (!tpl.attr) {
12293             // just itterate children..
12294             this.iterChild(node,this.compileNode);
12295             return;
12296         }
12297         tpl.uid = this.id++;
12298         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12299         node.removeAttribute('roo-'+ tpl.attr);
12300         if (tpl.attr != 'name') {
12301             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12302             node.parentNode.replaceChild(placeholder,  node);
12303         } else {
12304             
12305             var placeholder =  document.createElement('span');
12306             placeholder.className = 'roo-tpl-' + tpl.value;
12307             node.parentNode.replaceChild(placeholder,  node);
12308         }
12309         
12310         // parent now sees '{domtplXXXX}
12311         this.iterChild(node,this.compileNode);
12312         
12313         // we should now have node body...
12314         var div = document.createElement('div');
12315         div.appendChild(node);
12316         tpl.dom = node;
12317         // this has the unfortunate side effect of converting tagged attributes
12318         // eg. href="{...}" into %7C...%7D
12319         // this has been fixed by searching for those combo's although it's a bit hacky..
12320         
12321         
12322         tpl.body = div.innerHTML;
12323         
12324         
12325          
12326         tpl.id = tpl.uid;
12327         switch(tpl.attr) {
12328             case 'for' :
12329                 switch (tpl.value) {
12330                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12331                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12332                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12333                 }
12334                 break;
12335             
12336             case 'exec':
12337                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12338                 break;
12339             
12340             case 'if':     
12341                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12342                 break;
12343             
12344             case 'name':
12345                 tpl.id  = tpl.value; // replace non characters???
12346                 break;
12347             
12348         }
12349         
12350         
12351         this.tpls.push(tpl);
12352         
12353         
12354         
12355     },
12356     
12357     
12358     
12359     
12360     /**
12361      * Compile a segment of the template into a 'sub-template'
12362      *
12363      * 
12364      * 
12365      *
12366      */
12367     compileTpl : function(tpl)
12368     {
12369         var fm = Roo.util.Format;
12370         var useF = this.disableFormats !== true;
12371         
12372         var sep = Roo.isGecko ? "+\n" : ",\n";
12373         
12374         var undef = function(str) {
12375             Roo.debug && Roo.log("Property not found :"  + str);
12376             return '';
12377         };
12378           
12379         //Roo.log(tpl.body);
12380         
12381         
12382         
12383         var fn = function(m, lbrace, name, format, args)
12384         {
12385             //Roo.log("ARGS");
12386             //Roo.log(arguments);
12387             args = args ? args.replace(/\\'/g,"'") : args;
12388             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12389             if (typeof(format) == 'undefined') {
12390                 format =  'htmlEncode'; 
12391             }
12392             if (format == 'raw' ) {
12393                 format = false;
12394             }
12395             
12396             if(name.substr(0, 6) == 'domtpl'){
12397                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12398             }
12399             
12400             // build an array of options to determine if value is undefined..
12401             
12402             // basically get 'xxxx.yyyy' then do
12403             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12404             //    (function () { Roo.log("Property not found"); return ''; })() :
12405             //    ......
12406             
12407             var udef_ar = [];
12408             var lookfor = '';
12409             Roo.each(name.split('.'), function(st) {
12410                 lookfor += (lookfor.length ? '.': '') + st;
12411                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12412             });
12413             
12414             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12415             
12416             
12417             if(format && useF){
12418                 
12419                 args = args ? ',' + args : "";
12420                  
12421                 if(format.substr(0, 5) != "this."){
12422                     format = "fm." + format + '(';
12423                 }else{
12424                     format = 'this.call("'+ format.substr(5) + '", ';
12425                     args = ", values";
12426                 }
12427                 
12428                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12429             }
12430              
12431             if (args && args.length) {
12432                 // called with xxyx.yuu:(test,test)
12433                 // change to ()
12434                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12435             }
12436             // raw.. - :raw modifier..
12437             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12438             
12439         };
12440         var body;
12441         // branched to use + in gecko and [].join() in others
12442         if(Roo.isGecko){
12443             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12444                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12445                     "';};};";
12446         }else{
12447             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12448             body.push(tpl.body.replace(/(\r\n|\n)/g,
12449                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12450             body.push("'].join('');};};");
12451             body = body.join('');
12452         }
12453         
12454         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12455        
12456         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12457         eval(body);
12458         
12459         return this;
12460     },
12461      
12462     /**
12463      * same as applyTemplate, except it's done to one of the subTemplates
12464      * when using named templates, you can do:
12465      *
12466      * var str = pl.applySubTemplate('your-name', values);
12467      *
12468      * 
12469      * @param {Number} id of the template
12470      * @param {Object} values to apply to template
12471      * @param {Object} parent (normaly the instance of this object)
12472      */
12473     applySubTemplate : function(id, values, parent)
12474     {
12475         
12476         
12477         var t = this.tpls[id];
12478         
12479         
12480         try { 
12481             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12482                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12483                 return '';
12484             }
12485         } catch(e) {
12486             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12487             Roo.log(values);
12488           
12489             return '';
12490         }
12491         try { 
12492             
12493             if(t.execCall && t.execCall.call(this, values, parent)){
12494                 return '';
12495             }
12496         } catch(e) {
12497             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12498             Roo.log(values);
12499             return '';
12500         }
12501         
12502         try {
12503             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12504             parent = t.target ? values : parent;
12505             if(t.forCall && vs instanceof Array){
12506                 var buf = [];
12507                 for(var i = 0, len = vs.length; i < len; i++){
12508                     try {
12509                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12510                     } catch (e) {
12511                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12512                         Roo.log(e.body);
12513                         //Roo.log(t.compiled);
12514                         Roo.log(vs[i]);
12515                     }   
12516                 }
12517                 return buf.join('');
12518             }
12519         } catch (e) {
12520             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12521             Roo.log(values);
12522             return '';
12523         }
12524         try {
12525             return t.compiled.call(this, vs, parent);
12526         } catch (e) {
12527             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12528             Roo.log(e.body);
12529             //Roo.log(t.compiled);
12530             Roo.log(values);
12531             return '';
12532         }
12533     },
12534
12535    
12536
12537     applyTemplate : function(values){
12538         return this.master.compiled.call(this, values, {});
12539         //var s = this.subs;
12540     },
12541
12542     apply : function(){
12543         return this.applyTemplate.apply(this, arguments);
12544     }
12545
12546  });
12547
12548 Roo.DomTemplate.from = function(el){
12549     el = Roo.getDom(el);
12550     return new Roo.Domtemplate(el.value || el.innerHTML);
12551 };/*
12552  * Based on:
12553  * Ext JS Library 1.1.1
12554  * Copyright(c) 2006-2007, Ext JS, LLC.
12555  *
12556  * Originally Released Under LGPL - original licence link has changed is not relivant.
12557  *
12558  * Fork - LGPL
12559  * <script type="text/javascript">
12560  */
12561
12562 /**
12563  * @class Roo.util.DelayedTask
12564  * Provides a convenient method of performing setTimeout where a new
12565  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12566  * You can use this class to buffer
12567  * the keypress events for a certain number of milliseconds, and perform only if they stop
12568  * for that amount of time.
12569  * @constructor The parameters to this constructor serve as defaults and are not required.
12570  * @param {Function} fn (optional) The default function to timeout
12571  * @param {Object} scope (optional) The default scope of that timeout
12572  * @param {Array} args (optional) The default Array of arguments
12573  */
12574 Roo.util.DelayedTask = function(fn, scope, args){
12575     var id = null, d, t;
12576
12577     var call = function(){
12578         var now = new Date().getTime();
12579         if(now - t >= d){
12580             clearInterval(id);
12581             id = null;
12582             fn.apply(scope, args || []);
12583         }
12584     };
12585     /**
12586      * Cancels any pending timeout and queues a new one
12587      * @param {Number} delay The milliseconds to delay
12588      * @param {Function} newFn (optional) Overrides function passed to constructor
12589      * @param {Object} newScope (optional) Overrides scope passed to constructor
12590      * @param {Array} newArgs (optional) Overrides args passed to constructor
12591      */
12592     this.delay = function(delay, newFn, newScope, newArgs){
12593         if(id && delay != d){
12594             this.cancel();
12595         }
12596         d = delay;
12597         t = new Date().getTime();
12598         fn = newFn || fn;
12599         scope = newScope || scope;
12600         args = newArgs || args;
12601         if(!id){
12602             id = setInterval(call, d);
12603         }
12604     };
12605
12606     /**
12607      * Cancel the last queued timeout
12608      */
12609     this.cancel = function(){
12610         if(id){
12611             clearInterval(id);
12612             id = null;
12613         }
12614     };
12615 };/*
12616  * Based on:
12617  * Ext JS Library 1.1.1
12618  * Copyright(c) 2006-2007, Ext JS, LLC.
12619  *
12620  * Originally Released Under LGPL - original licence link has changed is not relivant.
12621  *
12622  * Fork - LGPL
12623  * <script type="text/javascript">
12624  */
12625  
12626  
12627 Roo.util.TaskRunner = function(interval){
12628     interval = interval || 10;
12629     var tasks = [], removeQueue = [];
12630     var id = 0;
12631     var running = false;
12632
12633     var stopThread = function(){
12634         running = false;
12635         clearInterval(id);
12636         id = 0;
12637     };
12638
12639     var startThread = function(){
12640         if(!running){
12641             running = true;
12642             id = setInterval(runTasks, interval);
12643         }
12644     };
12645
12646     var removeTask = function(task){
12647         removeQueue.push(task);
12648         if(task.onStop){
12649             task.onStop();
12650         }
12651     };
12652
12653     var runTasks = function(){
12654         if(removeQueue.length > 0){
12655             for(var i = 0, len = removeQueue.length; i < len; i++){
12656                 tasks.remove(removeQueue[i]);
12657             }
12658             removeQueue = [];
12659             if(tasks.length < 1){
12660                 stopThread();
12661                 return;
12662             }
12663         }
12664         var now = new Date().getTime();
12665         for(var i = 0, len = tasks.length; i < len; ++i){
12666             var t = tasks[i];
12667             var itime = now - t.taskRunTime;
12668             if(t.interval <= itime){
12669                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12670                 t.taskRunTime = now;
12671                 if(rt === false || t.taskRunCount === t.repeat){
12672                     removeTask(t);
12673                     return;
12674                 }
12675             }
12676             if(t.duration && t.duration <= (now - t.taskStartTime)){
12677                 removeTask(t);
12678             }
12679         }
12680     };
12681
12682     /**
12683      * Queues a new task.
12684      * @param {Object} task
12685      */
12686     this.start = function(task){
12687         tasks.push(task);
12688         task.taskStartTime = new Date().getTime();
12689         task.taskRunTime = 0;
12690         task.taskRunCount = 0;
12691         startThread();
12692         return task;
12693     };
12694
12695     this.stop = function(task){
12696         removeTask(task);
12697         return task;
12698     };
12699
12700     this.stopAll = function(){
12701         stopThread();
12702         for(var i = 0, len = tasks.length; i < len; i++){
12703             if(tasks[i].onStop){
12704                 tasks[i].onStop();
12705             }
12706         }
12707         tasks = [];
12708         removeQueue = [];
12709     };
12710 };
12711
12712 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12713  * Based on:
12714  * Ext JS Library 1.1.1
12715  * Copyright(c) 2006-2007, Ext JS, LLC.
12716  *
12717  * Originally Released Under LGPL - original licence link has changed is not relivant.
12718  *
12719  * Fork - LGPL
12720  * <script type="text/javascript">
12721  */
12722
12723  
12724 /**
12725  * @class Roo.util.MixedCollection
12726  * @extends Roo.util.Observable
12727  * A Collection class that maintains both numeric indexes and keys and exposes events.
12728  * @constructor
12729  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12730  * collection (defaults to false)
12731  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12732  * and return the key value for that item.  This is used when available to look up the key on items that
12733  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12734  * equivalent to providing an implementation for the {@link #getKey} method.
12735  */
12736 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12737     this.items = [];
12738     this.map = {};
12739     this.keys = [];
12740     this.length = 0;
12741     this.addEvents({
12742         /**
12743          * @event clear
12744          * Fires when the collection is cleared.
12745          */
12746         "clear" : true,
12747         /**
12748          * @event add
12749          * Fires when an item is added to the collection.
12750          * @param {Number} index The index at which the item was added.
12751          * @param {Object} o The item added.
12752          * @param {String} key The key associated with the added item.
12753          */
12754         "add" : true,
12755         /**
12756          * @event replace
12757          * Fires when an item is replaced in the collection.
12758          * @param {String} key he key associated with the new added.
12759          * @param {Object} old The item being replaced.
12760          * @param {Object} new The new item.
12761          */
12762         "replace" : true,
12763         /**
12764          * @event remove
12765          * Fires when an item is removed from the collection.
12766          * @param {Object} o The item being removed.
12767          * @param {String} key (optional) The key associated with the removed item.
12768          */
12769         "remove" : true,
12770         "sort" : true
12771     });
12772     this.allowFunctions = allowFunctions === true;
12773     if(keyFn){
12774         this.getKey = keyFn;
12775     }
12776     Roo.util.MixedCollection.superclass.constructor.call(this);
12777 };
12778
12779 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12780     allowFunctions : false,
12781     
12782 /**
12783  * Adds an item to the collection.
12784  * @param {String} key The key to associate with the item
12785  * @param {Object} o The item to add.
12786  * @return {Object} The item added.
12787  */
12788     add : function(key, o){
12789         if(arguments.length == 1){
12790             o = arguments[0];
12791             key = this.getKey(o);
12792         }
12793         if(typeof key == "undefined" || key === null){
12794             this.length++;
12795             this.items.push(o);
12796             this.keys.push(null);
12797         }else{
12798             var old = this.map[key];
12799             if(old){
12800                 return this.replace(key, o);
12801             }
12802             this.length++;
12803             this.items.push(o);
12804             this.map[key] = o;
12805             this.keys.push(key);
12806         }
12807         this.fireEvent("add", this.length-1, o, key);
12808         return o;
12809     },
12810        
12811 /**
12812   * MixedCollection has a generic way to fetch keys if you implement getKey.
12813 <pre><code>
12814 // normal way
12815 var mc = new Roo.util.MixedCollection();
12816 mc.add(someEl.dom.id, someEl);
12817 mc.add(otherEl.dom.id, otherEl);
12818 //and so on
12819
12820 // using getKey
12821 var mc = new Roo.util.MixedCollection();
12822 mc.getKey = function(el){
12823    return el.dom.id;
12824 };
12825 mc.add(someEl);
12826 mc.add(otherEl);
12827
12828 // or via the constructor
12829 var mc = new Roo.util.MixedCollection(false, function(el){
12830    return el.dom.id;
12831 });
12832 mc.add(someEl);
12833 mc.add(otherEl);
12834 </code></pre>
12835  * @param o {Object} The item for which to find the key.
12836  * @return {Object} The key for the passed item.
12837  */
12838     getKey : function(o){
12839          return o.id; 
12840     },
12841    
12842 /**
12843  * Replaces an item in the collection.
12844  * @param {String} key The key associated with the item to replace, or the item to replace.
12845  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12846  * @return {Object}  The new item.
12847  */
12848     replace : function(key, o){
12849         if(arguments.length == 1){
12850             o = arguments[0];
12851             key = this.getKey(o);
12852         }
12853         var old = this.item(key);
12854         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12855              return this.add(key, o);
12856         }
12857         var index = this.indexOfKey(key);
12858         this.items[index] = o;
12859         this.map[key] = o;
12860         this.fireEvent("replace", key, old, o);
12861         return o;
12862     },
12863    
12864 /**
12865  * Adds all elements of an Array or an Object to the collection.
12866  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12867  * an Array of values, each of which are added to the collection.
12868  */
12869     addAll : function(objs){
12870         if(arguments.length > 1 || objs instanceof Array){
12871             var args = arguments.length > 1 ? arguments : objs;
12872             for(var i = 0, len = args.length; i < len; i++){
12873                 this.add(args[i]);
12874             }
12875         }else{
12876             for(var key in objs){
12877                 if(this.allowFunctions || typeof objs[key] != "function"){
12878                     this.add(key, objs[key]);
12879                 }
12880             }
12881         }
12882     },
12883    
12884 /**
12885  * Executes the specified function once for every item in the collection, passing each
12886  * item as the first and only parameter. returning false from the function will stop the iteration.
12887  * @param {Function} fn The function to execute for each item.
12888  * @param {Object} scope (optional) The scope in which to execute the function.
12889  */
12890     each : function(fn, scope){
12891         var items = [].concat(this.items); // each safe for removal
12892         for(var i = 0, len = items.length; i < len; i++){
12893             if(fn.call(scope || items[i], items[i], i, len) === false){
12894                 break;
12895             }
12896         }
12897     },
12898    
12899 /**
12900  * Executes the specified function once for every key in the collection, passing each
12901  * key, and its associated item as the first two parameters.
12902  * @param {Function} fn The function to execute for each item.
12903  * @param {Object} scope (optional) The scope in which to execute the function.
12904  */
12905     eachKey : function(fn, scope){
12906         for(var i = 0, len = this.keys.length; i < len; i++){
12907             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12908         }
12909     },
12910    
12911 /**
12912  * Returns the first item in the collection which elicits a true return value from the
12913  * passed selection function.
12914  * @param {Function} fn The selection function to execute for each item.
12915  * @param {Object} scope (optional) The scope in which to execute the function.
12916  * @return {Object} The first item in the collection which returned true from the selection function.
12917  */
12918     find : function(fn, scope){
12919         for(var i = 0, len = this.items.length; i < len; i++){
12920             if(fn.call(scope || window, this.items[i], this.keys[i])){
12921                 return this.items[i];
12922             }
12923         }
12924         return null;
12925     },
12926    
12927 /**
12928  * Inserts an item at the specified index in the collection.
12929  * @param {Number} index The index to insert the item at.
12930  * @param {String} key The key to associate with the new item, or the item itself.
12931  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12932  * @return {Object} The item inserted.
12933  */
12934     insert : function(index, key, o){
12935         if(arguments.length == 2){
12936             o = arguments[1];
12937             key = this.getKey(o);
12938         }
12939         if(index >= this.length){
12940             return this.add(key, o);
12941         }
12942         this.length++;
12943         this.items.splice(index, 0, o);
12944         if(typeof key != "undefined" && key != null){
12945             this.map[key] = o;
12946         }
12947         this.keys.splice(index, 0, key);
12948         this.fireEvent("add", index, o, key);
12949         return o;
12950     },
12951    
12952 /**
12953  * Removed an item from the collection.
12954  * @param {Object} o The item to remove.
12955  * @return {Object} The item removed.
12956  */
12957     remove : function(o){
12958         return this.removeAt(this.indexOf(o));
12959     },
12960    
12961 /**
12962  * Remove an item from a specified index in the collection.
12963  * @param {Number} index The index within the collection of the item to remove.
12964  */
12965     removeAt : function(index){
12966         if(index < this.length && index >= 0){
12967             this.length--;
12968             var o = this.items[index];
12969             this.items.splice(index, 1);
12970             var key = this.keys[index];
12971             if(typeof key != "undefined"){
12972                 delete this.map[key];
12973             }
12974             this.keys.splice(index, 1);
12975             this.fireEvent("remove", o, key);
12976         }
12977     },
12978    
12979 /**
12980  * Removed an item associated with the passed key fom the collection.
12981  * @param {String} key The key of the item to remove.
12982  */
12983     removeKey : function(key){
12984         return this.removeAt(this.indexOfKey(key));
12985     },
12986    
12987 /**
12988  * Returns the number of items in the collection.
12989  * @return {Number} the number of items in the collection.
12990  */
12991     getCount : function(){
12992         return this.length; 
12993     },
12994    
12995 /**
12996  * Returns index within the collection of the passed Object.
12997  * @param {Object} o The item to find the index of.
12998  * @return {Number} index of the item.
12999  */
13000     indexOf : function(o){
13001         if(!this.items.indexOf){
13002             for(var i = 0, len = this.items.length; i < len; i++){
13003                 if(this.items[i] == o) return i;
13004             }
13005             return -1;
13006         }else{
13007             return this.items.indexOf(o);
13008         }
13009     },
13010    
13011 /**
13012  * Returns index within the collection of the passed key.
13013  * @param {String} key The key to find the index of.
13014  * @return {Number} index of the key.
13015  */
13016     indexOfKey : function(key){
13017         if(!this.keys.indexOf){
13018             for(var i = 0, len = this.keys.length; i < len; i++){
13019                 if(this.keys[i] == key) return i;
13020             }
13021             return -1;
13022         }else{
13023             return this.keys.indexOf(key);
13024         }
13025     },
13026    
13027 /**
13028  * Returns the item associated with the passed key OR index. Key has priority over index.
13029  * @param {String/Number} key The key or index of the item.
13030  * @return {Object} The item associated with the passed key.
13031  */
13032     item : function(key){
13033         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13034         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13035     },
13036     
13037 /**
13038  * Returns the item at the specified index.
13039  * @param {Number} index The index of the item.
13040  * @return {Object}
13041  */
13042     itemAt : function(index){
13043         return this.items[index];
13044     },
13045     
13046 /**
13047  * Returns the item associated with the passed key.
13048  * @param {String/Number} key The key of the item.
13049  * @return {Object} The item associated with the passed key.
13050  */
13051     key : function(key){
13052         return this.map[key];
13053     },
13054    
13055 /**
13056  * Returns true if the collection contains the passed Object as an item.
13057  * @param {Object} o  The Object to look for in the collection.
13058  * @return {Boolean} True if the collection contains the Object as an item.
13059  */
13060     contains : function(o){
13061         return this.indexOf(o) != -1;
13062     },
13063    
13064 /**
13065  * Returns true if the collection contains the passed Object as a key.
13066  * @param {String} key The key to look for in the collection.
13067  * @return {Boolean} True if the collection contains the Object as a key.
13068  */
13069     containsKey : function(key){
13070         return typeof this.map[key] != "undefined";
13071     },
13072    
13073 /**
13074  * Removes all items from the collection.
13075  */
13076     clear : function(){
13077         this.length = 0;
13078         this.items = [];
13079         this.keys = [];
13080         this.map = {};
13081         this.fireEvent("clear");
13082     },
13083    
13084 /**
13085  * Returns the first item in the collection.
13086  * @return {Object} the first item in the collection..
13087  */
13088     first : function(){
13089         return this.items[0]; 
13090     },
13091    
13092 /**
13093  * Returns the last item in the collection.
13094  * @return {Object} the last item in the collection..
13095  */
13096     last : function(){
13097         return this.items[this.length-1];   
13098     },
13099     
13100     _sort : function(property, dir, fn){
13101         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13102         fn = fn || function(a, b){
13103             return a-b;
13104         };
13105         var c = [], k = this.keys, items = this.items;
13106         for(var i = 0, len = items.length; i < len; i++){
13107             c[c.length] = {key: k[i], value: items[i], index: i};
13108         }
13109         c.sort(function(a, b){
13110             var v = fn(a[property], b[property]) * dsc;
13111             if(v == 0){
13112                 v = (a.index < b.index ? -1 : 1);
13113             }
13114             return v;
13115         });
13116         for(var i = 0, len = c.length; i < len; i++){
13117             items[i] = c[i].value;
13118             k[i] = c[i].key;
13119         }
13120         this.fireEvent("sort", this);
13121     },
13122     
13123     /**
13124      * Sorts this collection with the passed comparison function
13125      * @param {String} direction (optional) "ASC" or "DESC"
13126      * @param {Function} fn (optional) comparison function
13127      */
13128     sort : function(dir, fn){
13129         this._sort("value", dir, fn);
13130     },
13131     
13132     /**
13133      * Sorts this collection by keys
13134      * @param {String} direction (optional) "ASC" or "DESC"
13135      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13136      */
13137     keySort : function(dir, fn){
13138         this._sort("key", dir, fn || function(a, b){
13139             return String(a).toUpperCase()-String(b).toUpperCase();
13140         });
13141     },
13142     
13143     /**
13144      * Returns a range of items in this collection
13145      * @param {Number} startIndex (optional) defaults to 0
13146      * @param {Number} endIndex (optional) default to the last item
13147      * @return {Array} An array of items
13148      */
13149     getRange : function(start, end){
13150         var items = this.items;
13151         if(items.length < 1){
13152             return [];
13153         }
13154         start = start || 0;
13155         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13156         var r = [];
13157         if(start <= end){
13158             for(var i = start; i <= end; i++) {
13159                     r[r.length] = items[i];
13160             }
13161         }else{
13162             for(var i = start; i >= end; i--) {
13163                     r[r.length] = items[i];
13164             }
13165         }
13166         return r;
13167     },
13168         
13169     /**
13170      * Filter the <i>objects</i> in this collection by a specific property. 
13171      * Returns a new collection that has been filtered.
13172      * @param {String} property A property on your objects
13173      * @param {String/RegExp} value Either string that the property values 
13174      * should start with or a RegExp to test against the property
13175      * @return {MixedCollection} The new filtered collection
13176      */
13177     filter : function(property, value){
13178         if(!value.exec){ // not a regex
13179             value = String(value);
13180             if(value.length == 0){
13181                 return this.clone();
13182             }
13183             value = new RegExp("^" + Roo.escapeRe(value), "i");
13184         }
13185         return this.filterBy(function(o){
13186             return o && value.test(o[property]);
13187         });
13188         },
13189     
13190     /**
13191      * Filter by a function. * Returns a new collection that has been filtered.
13192      * The passed function will be called with each 
13193      * object in the collection. If the function returns true, the value is included 
13194      * otherwise it is filtered.
13195      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13196      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13197      * @return {MixedCollection} The new filtered collection
13198      */
13199     filterBy : function(fn, scope){
13200         var r = new Roo.util.MixedCollection();
13201         r.getKey = this.getKey;
13202         var k = this.keys, it = this.items;
13203         for(var i = 0, len = it.length; i < len; i++){
13204             if(fn.call(scope||this, it[i], k[i])){
13205                                 r.add(k[i], it[i]);
13206                         }
13207         }
13208         return r;
13209     },
13210     
13211     /**
13212      * Creates a duplicate of this collection
13213      * @return {MixedCollection}
13214      */
13215     clone : function(){
13216         var r = new Roo.util.MixedCollection();
13217         var k = this.keys, it = this.items;
13218         for(var i = 0, len = it.length; i < len; i++){
13219             r.add(k[i], it[i]);
13220         }
13221         r.getKey = this.getKey;
13222         return r;
13223     }
13224 });
13225 /**
13226  * Returns the item associated with the passed key or index.
13227  * @method
13228  * @param {String/Number} key The key or index of the item.
13229  * @return {Object} The item associated with the passed key.
13230  */
13231 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13232  * Based on:
13233  * Ext JS Library 1.1.1
13234  * Copyright(c) 2006-2007, Ext JS, LLC.
13235  *
13236  * Originally Released Under LGPL - original licence link has changed is not relivant.
13237  *
13238  * Fork - LGPL
13239  * <script type="text/javascript">
13240  */
13241 /**
13242  * @class Roo.util.JSON
13243  * Modified version of Douglas Crockford"s json.js that doesn"t
13244  * mess with the Object prototype 
13245  * http://www.json.org/js.html
13246  * @singleton
13247  */
13248 Roo.util.JSON = new (function(){
13249     var useHasOwn = {}.hasOwnProperty ? true : false;
13250     
13251     // crashes Safari in some instances
13252     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13253     
13254     var pad = function(n) {
13255         return n < 10 ? "0" + n : n;
13256     };
13257     
13258     var m = {
13259         "\b": '\\b',
13260         "\t": '\\t',
13261         "\n": '\\n',
13262         "\f": '\\f',
13263         "\r": '\\r',
13264         '"' : '\\"',
13265         "\\": '\\\\'
13266     };
13267
13268     var encodeString = function(s){
13269         if (/["\\\x00-\x1f]/.test(s)) {
13270             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13271                 var c = m[b];
13272                 if(c){
13273                     return c;
13274                 }
13275                 c = b.charCodeAt();
13276                 return "\\u00" +
13277                     Math.floor(c / 16).toString(16) +
13278                     (c % 16).toString(16);
13279             }) + '"';
13280         }
13281         return '"' + s + '"';
13282     };
13283     
13284     var encodeArray = function(o){
13285         var a = ["["], b, i, l = o.length, v;
13286             for (i = 0; i < l; i += 1) {
13287                 v = o[i];
13288                 switch (typeof v) {
13289                     case "undefined":
13290                     case "function":
13291                     case "unknown":
13292                         break;
13293                     default:
13294                         if (b) {
13295                             a.push(',');
13296                         }
13297                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13298                         b = true;
13299                 }
13300             }
13301             a.push("]");
13302             return a.join("");
13303     };
13304     
13305     var encodeDate = function(o){
13306         return '"' + o.getFullYear() + "-" +
13307                 pad(o.getMonth() + 1) + "-" +
13308                 pad(o.getDate()) + "T" +
13309                 pad(o.getHours()) + ":" +
13310                 pad(o.getMinutes()) + ":" +
13311                 pad(o.getSeconds()) + '"';
13312     };
13313     
13314     /**
13315      * Encodes an Object, Array or other value
13316      * @param {Mixed} o The variable to encode
13317      * @return {String} The JSON string
13318      */
13319     this.encode = function(o)
13320     {
13321         // should this be extended to fully wrap stringify..
13322         
13323         if(typeof o == "undefined" || o === null){
13324             return "null";
13325         }else if(o instanceof Array){
13326             return encodeArray(o);
13327         }else if(o instanceof Date){
13328             return encodeDate(o);
13329         }else if(typeof o == "string"){
13330             return encodeString(o);
13331         }else if(typeof o == "number"){
13332             return isFinite(o) ? String(o) : "null";
13333         }else if(typeof o == "boolean"){
13334             return String(o);
13335         }else {
13336             var a = ["{"], b, i, v;
13337             for (i in o) {
13338                 if(!useHasOwn || o.hasOwnProperty(i)) {
13339                     v = o[i];
13340                     switch (typeof v) {
13341                     case "undefined":
13342                     case "function":
13343                     case "unknown":
13344                         break;
13345                     default:
13346                         if(b){
13347                             a.push(',');
13348                         }
13349                         a.push(this.encode(i), ":",
13350                                 v === null ? "null" : this.encode(v));
13351                         b = true;
13352                     }
13353                 }
13354             }
13355             a.push("}");
13356             return a.join("");
13357         }
13358     };
13359     
13360     /**
13361      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13362      * @param {String} json The JSON string
13363      * @return {Object} The resulting object
13364      */
13365     this.decode = function(json){
13366         
13367         return  /** eval:var:json */ eval("(" + json + ')');
13368     };
13369 })();
13370 /** 
13371  * Shorthand for {@link Roo.util.JSON#encode}
13372  * @member Roo encode 
13373  * @method */
13374 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13375 /** 
13376  * Shorthand for {@link Roo.util.JSON#decode}
13377  * @member Roo decode 
13378  * @method */
13379 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13380 /*
13381  * Based on:
13382  * Ext JS Library 1.1.1
13383  * Copyright(c) 2006-2007, Ext JS, LLC.
13384  *
13385  * Originally Released Under LGPL - original licence link has changed is not relivant.
13386  *
13387  * Fork - LGPL
13388  * <script type="text/javascript">
13389  */
13390  
13391 /**
13392  * @class Roo.util.Format
13393  * Reusable data formatting functions
13394  * @singleton
13395  */
13396 Roo.util.Format = function(){
13397     var trimRe = /^\s+|\s+$/g;
13398     return {
13399         /**
13400          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13401          * @param {String} value The string to truncate
13402          * @param {Number} length The maximum length to allow before truncating
13403          * @return {String} The converted text
13404          */
13405         ellipsis : function(value, len){
13406             if(value && value.length > len){
13407                 return value.substr(0, len-3)+"...";
13408             }
13409             return value;
13410         },
13411
13412         /**
13413          * Checks a reference and converts it to empty string if it is undefined
13414          * @param {Mixed} value Reference to check
13415          * @return {Mixed} Empty string if converted, otherwise the original value
13416          */
13417         undef : function(value){
13418             return typeof value != "undefined" ? value : "";
13419         },
13420
13421         /**
13422          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13423          * @param {String} value The string to encode
13424          * @return {String} The encoded text
13425          */
13426         htmlEncode : function(value){
13427             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13428         },
13429
13430         /**
13431          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13432          * @param {String} value The string to decode
13433          * @return {String} The decoded text
13434          */
13435         htmlDecode : function(value){
13436             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13437         },
13438
13439         /**
13440          * Trims any whitespace from either side of a string
13441          * @param {String} value The text to trim
13442          * @return {String} The trimmed text
13443          */
13444         trim : function(value){
13445             return String(value).replace(trimRe, "");
13446         },
13447
13448         /**
13449          * Returns a substring from within an original string
13450          * @param {String} value The original text
13451          * @param {Number} start The start index of the substring
13452          * @param {Number} length The length of the substring
13453          * @return {String} The substring
13454          */
13455         substr : function(value, start, length){
13456             return String(value).substr(start, length);
13457         },
13458
13459         /**
13460          * Converts a string to all lower case letters
13461          * @param {String} value The text to convert
13462          * @return {String} The converted text
13463          */
13464         lowercase : function(value){
13465             return String(value).toLowerCase();
13466         },
13467
13468         /**
13469          * Converts a string to all upper case letters
13470          * @param {String} value The text to convert
13471          * @return {String} The converted text
13472          */
13473         uppercase : function(value){
13474             return String(value).toUpperCase();
13475         },
13476
13477         /**
13478          * Converts the first character only of a string to upper case
13479          * @param {String} value The text to convert
13480          * @return {String} The converted text
13481          */
13482         capitalize : function(value){
13483             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13484         },
13485
13486         // private
13487         call : function(value, fn){
13488             if(arguments.length > 2){
13489                 var args = Array.prototype.slice.call(arguments, 2);
13490                 args.unshift(value);
13491                  
13492                 return /** eval:var:value */  eval(fn).apply(window, args);
13493             }else{
13494                 /** eval:var:value */
13495                 return /** eval:var:value */ eval(fn).call(window, value);
13496             }
13497         },
13498
13499        
13500         /**
13501          * safer version of Math.toFixed..??/
13502          * @param {Number/String} value The numeric value to format
13503          * @param {Number/String} value Decimal places 
13504          * @return {String} The formatted currency string
13505          */
13506         toFixed : function(v, n)
13507         {
13508             // why not use to fixed - precision is buggered???
13509             if (!n) {
13510                 return Math.round(v-0);
13511             }
13512             var fact = Math.pow(10,n+1);
13513             v = (Math.round((v-0)*fact))/fact;
13514             var z = (''+fact).substring(2);
13515             if (v == Math.floor(v)) {
13516                 return Math.floor(v) + '.' + z;
13517             }
13518             
13519             // now just padd decimals..
13520             var ps = String(v).split('.');
13521             var fd = (ps[1] + z);
13522             var r = fd.substring(0,n); 
13523             var rm = fd.substring(n); 
13524             if (rm < 5) {
13525                 return ps[0] + '.' + r;
13526             }
13527             r*=1; // turn it into a number;
13528             r++;
13529             if (String(r).length != n) {
13530                 ps[0]*=1;
13531                 ps[0]++;
13532                 r = String(r).substring(1); // chop the end off.
13533             }
13534             
13535             return ps[0] + '.' + r;
13536              
13537         },
13538         
13539         /**
13540          * Format a number as US currency
13541          * @param {Number/String} value The numeric value to format
13542          * @return {String} The formatted currency string
13543          */
13544         usMoney : function(v){
13545             return '$' + Roo.util.Format.number(v);
13546         },
13547         
13548         /**
13549          * Format a number
13550          * eventually this should probably emulate php's number_format
13551          * @param {Number/String} value The numeric value to format
13552          * @param {Number} decimals number of decimal places
13553          * @return {String} The formatted currency string
13554          */
13555         number : function(v,decimals)
13556         {
13557             // multiply and round.
13558             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13559             var mul = Math.pow(10, decimals);
13560             var zero = String(mul).substring(1);
13561             v = (Math.round((v-0)*mul))/mul;
13562             
13563             // if it's '0' number.. then
13564             
13565             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13566             v = String(v);
13567             var ps = v.split('.');
13568             var whole = ps[0];
13569             
13570             
13571             var r = /(\d+)(\d{3})/;
13572             // add comma's
13573             while (r.test(whole)) {
13574                 whole = whole.replace(r, '$1' + ',' + '$2');
13575             }
13576             
13577             
13578             var sub = ps[1] ?
13579                     // has decimals..
13580                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13581                     // does not have decimals
13582                     (decimals ? ('.' + zero) : '');
13583             
13584             
13585             return whole + sub ;
13586         },
13587         
13588         /**
13589          * Parse a value into a formatted date using the specified format pattern.
13590          * @param {Mixed} value The value to format
13591          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13592          * @return {String} The formatted date string
13593          */
13594         date : function(v, format){
13595             if(!v){
13596                 return "";
13597             }
13598             if(!(v instanceof Date)){
13599                 v = new Date(Date.parse(v));
13600             }
13601             return v.dateFormat(format || "m/d/Y");
13602         },
13603
13604         /**
13605          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13606          * @param {String} format Any valid date format string
13607          * @return {Function} The date formatting function
13608          */
13609         dateRenderer : function(format){
13610             return function(v){
13611                 return Roo.util.Format.date(v, format);  
13612             };
13613         },
13614
13615         // private
13616         stripTagsRE : /<\/?[^>]+>/gi,
13617         
13618         /**
13619          * Strips all HTML tags
13620          * @param {Mixed} value The text from which to strip tags
13621          * @return {String} The stripped text
13622          */
13623         stripTags : function(v){
13624             return !v ? v : String(v).replace(this.stripTagsRE, "");
13625         }
13626     };
13627 }();/*
13628  * Based on:
13629  * Ext JS Library 1.1.1
13630  * Copyright(c) 2006-2007, Ext JS, LLC.
13631  *
13632  * Originally Released Under LGPL - original licence link has changed is not relivant.
13633  *
13634  * Fork - LGPL
13635  * <script type="text/javascript">
13636  */
13637
13638
13639  
13640
13641 /**
13642  * @class Roo.MasterTemplate
13643  * @extends Roo.Template
13644  * Provides a template that can have child templates. The syntax is:
13645 <pre><code>
13646 var t = new Roo.MasterTemplate(
13647         '&lt;select name="{name}"&gt;',
13648                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13649         '&lt;/select&gt;'
13650 );
13651 t.add('options', {value: 'foo', text: 'bar'});
13652 // or you can add multiple child elements in one shot
13653 t.addAll('options', [
13654     {value: 'foo', text: 'bar'},
13655     {value: 'foo2', text: 'bar2'},
13656     {value: 'foo3', text: 'bar3'}
13657 ]);
13658 // then append, applying the master template values
13659 t.append('my-form', {name: 'my-select'});
13660 </code></pre>
13661 * A name attribute for the child template is not required if you have only one child
13662 * template or you want to refer to them by index.
13663  */
13664 Roo.MasterTemplate = function(){
13665     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13666     this.originalHtml = this.html;
13667     var st = {};
13668     var m, re = this.subTemplateRe;
13669     re.lastIndex = 0;
13670     var subIndex = 0;
13671     while(m = re.exec(this.html)){
13672         var name = m[1], content = m[2];
13673         st[subIndex] = {
13674             name: name,
13675             index: subIndex,
13676             buffer: [],
13677             tpl : new Roo.Template(content)
13678         };
13679         if(name){
13680             st[name] = st[subIndex];
13681         }
13682         st[subIndex].tpl.compile();
13683         st[subIndex].tpl.call = this.call.createDelegate(this);
13684         subIndex++;
13685     }
13686     this.subCount = subIndex;
13687     this.subs = st;
13688 };
13689 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13690     /**
13691     * The regular expression used to match sub templates
13692     * @type RegExp
13693     * @property
13694     */
13695     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13696
13697     /**
13698      * Applies the passed values to a child template.
13699      * @param {String/Number} name (optional) The name or index of the child template
13700      * @param {Array/Object} values The values to be applied to the template
13701      * @return {MasterTemplate} this
13702      */
13703      add : function(name, values){
13704         if(arguments.length == 1){
13705             values = arguments[0];
13706             name = 0;
13707         }
13708         var s = this.subs[name];
13709         s.buffer[s.buffer.length] = s.tpl.apply(values);
13710         return this;
13711     },
13712
13713     /**
13714      * Applies all the passed values to a child template.
13715      * @param {String/Number} name (optional) The name or index of the child template
13716      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13717      * @param {Boolean} reset (optional) True to reset the template first
13718      * @return {MasterTemplate} this
13719      */
13720     fill : function(name, values, reset){
13721         var a = arguments;
13722         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13723             values = a[0];
13724             name = 0;
13725             reset = a[1];
13726         }
13727         if(reset){
13728             this.reset();
13729         }
13730         for(var i = 0, len = values.length; i < len; i++){
13731             this.add(name, values[i]);
13732         }
13733         return this;
13734     },
13735
13736     /**
13737      * Resets the template for reuse
13738      * @return {MasterTemplate} this
13739      */
13740      reset : function(){
13741         var s = this.subs;
13742         for(var i = 0; i < this.subCount; i++){
13743             s[i].buffer = [];
13744         }
13745         return this;
13746     },
13747
13748     applyTemplate : function(values){
13749         var s = this.subs;
13750         var replaceIndex = -1;
13751         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13752             return s[++replaceIndex].buffer.join("");
13753         });
13754         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13755     },
13756
13757     apply : function(){
13758         return this.applyTemplate.apply(this, arguments);
13759     },
13760
13761     compile : function(){return this;}
13762 });
13763
13764 /**
13765  * Alias for fill().
13766  * @method
13767  */
13768 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13769  /**
13770  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13771  * var tpl = Roo.MasterTemplate.from('element-id');
13772  * @param {String/HTMLElement} el
13773  * @param {Object} config
13774  * @static
13775  */
13776 Roo.MasterTemplate.from = function(el, config){
13777     el = Roo.getDom(el);
13778     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13779 };/*
13780  * Based on:
13781  * Ext JS Library 1.1.1
13782  * Copyright(c) 2006-2007, Ext JS, LLC.
13783  *
13784  * Originally Released Under LGPL - original licence link has changed is not relivant.
13785  *
13786  * Fork - LGPL
13787  * <script type="text/javascript">
13788  */
13789
13790  
13791 /**
13792  * @class Roo.util.CSS
13793  * Utility class for manipulating CSS rules
13794  * @singleton
13795  */
13796 Roo.util.CSS = function(){
13797         var rules = null;
13798         var doc = document;
13799
13800     var camelRe = /(-[a-z])/gi;
13801     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13802
13803    return {
13804    /**
13805     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13806     * tag and appended to the HEAD of the document.
13807     * @param {String|Object} cssText The text containing the css rules
13808     * @param {String} id An id to add to the stylesheet for later removal
13809     * @return {StyleSheet}
13810     */
13811     createStyleSheet : function(cssText, id){
13812         var ss;
13813         var head = doc.getElementsByTagName("head")[0];
13814         var nrules = doc.createElement("style");
13815         nrules.setAttribute("type", "text/css");
13816         if(id){
13817             nrules.setAttribute("id", id);
13818         }
13819         if (typeof(cssText) != 'string') {
13820             // support object maps..
13821             // not sure if this a good idea.. 
13822             // perhaps it should be merged with the general css handling
13823             // and handle js style props.
13824             var cssTextNew = [];
13825             for(var n in cssText) {
13826                 var citems = [];
13827                 for(var k in cssText[n]) {
13828                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13829                 }
13830                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13831                 
13832             }
13833             cssText = cssTextNew.join("\n");
13834             
13835         }
13836        
13837        
13838        if(Roo.isIE){
13839            head.appendChild(nrules);
13840            ss = nrules.styleSheet;
13841            ss.cssText = cssText;
13842        }else{
13843            try{
13844                 nrules.appendChild(doc.createTextNode(cssText));
13845            }catch(e){
13846                nrules.cssText = cssText; 
13847            }
13848            head.appendChild(nrules);
13849            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13850        }
13851        this.cacheStyleSheet(ss);
13852        return ss;
13853    },
13854
13855    /**
13856     * Removes a style or link tag by id
13857     * @param {String} id The id of the tag
13858     */
13859    removeStyleSheet : function(id){
13860        var existing = doc.getElementById(id);
13861        if(existing){
13862            existing.parentNode.removeChild(existing);
13863        }
13864    },
13865
13866    /**
13867     * Dynamically swaps an existing stylesheet reference for a new one
13868     * @param {String} id The id of an existing link tag to remove
13869     * @param {String} url The href of the new stylesheet to include
13870     */
13871    swapStyleSheet : function(id, url){
13872        this.removeStyleSheet(id);
13873        var ss = doc.createElement("link");
13874        ss.setAttribute("rel", "stylesheet");
13875        ss.setAttribute("type", "text/css");
13876        ss.setAttribute("id", id);
13877        ss.setAttribute("href", url);
13878        doc.getElementsByTagName("head")[0].appendChild(ss);
13879    },
13880    
13881    /**
13882     * Refresh the rule cache if you have dynamically added stylesheets
13883     * @return {Object} An object (hash) of rules indexed by selector
13884     */
13885    refreshCache : function(){
13886        return this.getRules(true);
13887    },
13888
13889    // private
13890    cacheStyleSheet : function(stylesheet){
13891        if(!rules){
13892            rules = {};
13893        }
13894        try{// try catch for cross domain access issue
13895            var ssRules = stylesheet.cssRules || stylesheet.rules;
13896            for(var j = ssRules.length-1; j >= 0; --j){
13897                rules[ssRules[j].selectorText] = ssRules[j];
13898            }
13899        }catch(e){}
13900    },
13901    
13902    /**
13903     * Gets all css rules for the document
13904     * @param {Boolean} refreshCache true to refresh the internal cache
13905     * @return {Object} An object (hash) of rules indexed by selector
13906     */
13907    getRules : function(refreshCache){
13908                 if(rules == null || refreshCache){
13909                         rules = {};
13910                         var ds = doc.styleSheets;
13911                         for(var i =0, len = ds.length; i < len; i++){
13912                             try{
13913                         this.cacheStyleSheet(ds[i]);
13914                     }catch(e){} 
13915                 }
13916                 }
13917                 return rules;
13918         },
13919         
13920         /**
13921     * Gets an an individual CSS rule by selector(s)
13922     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13923     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13924     * @return {CSSRule} The CSS rule or null if one is not found
13925     */
13926    getRule : function(selector, refreshCache){
13927                 var rs = this.getRules(refreshCache);
13928                 if(!(selector instanceof Array)){
13929                     return rs[selector];
13930                 }
13931                 for(var i = 0; i < selector.length; i++){
13932                         if(rs[selector[i]]){
13933                                 return rs[selector[i]];
13934                         }
13935                 }
13936                 return null;
13937         },
13938         
13939         
13940         /**
13941     * Updates a rule property
13942     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13943     * @param {String} property The css property
13944     * @param {String} value The new value for the property
13945     * @return {Boolean} true If a rule was found and updated
13946     */
13947    updateRule : function(selector, property, value){
13948                 if(!(selector instanceof Array)){
13949                         var rule = this.getRule(selector);
13950                         if(rule){
13951                                 rule.style[property.replace(camelRe, camelFn)] = value;
13952                                 return true;
13953                         }
13954                 }else{
13955                         for(var i = 0; i < selector.length; i++){
13956                                 if(this.updateRule(selector[i], property, value)){
13957                                         return true;
13958                                 }
13959                         }
13960                 }
13961                 return false;
13962         }
13963    };   
13964 }();/*
13965  * Based on:
13966  * Ext JS Library 1.1.1
13967  * Copyright(c) 2006-2007, Ext JS, LLC.
13968  *
13969  * Originally Released Under LGPL - original licence link has changed is not relivant.
13970  *
13971  * Fork - LGPL
13972  * <script type="text/javascript">
13973  */
13974
13975  
13976
13977 /**
13978  * @class Roo.util.ClickRepeater
13979  * @extends Roo.util.Observable
13980  * 
13981  * A wrapper class which can be applied to any element. Fires a "click" event while the
13982  * mouse is pressed. The interval between firings may be specified in the config but
13983  * defaults to 10 milliseconds.
13984  * 
13985  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13986  * 
13987  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13988  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13989  * Similar to an autorepeat key delay.
13990  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13991  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13992  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13993  *           "interval" and "delay" are ignored. "immediate" is honored.
13994  * @cfg {Boolean} preventDefault True to prevent the default click event
13995  * @cfg {Boolean} stopDefault True to stop the default click event
13996  * 
13997  * @history
13998  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13999  *     2007-02-02 jvs Renamed to ClickRepeater
14000  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14001  *
14002  *  @constructor
14003  * @param {String/HTMLElement/Element} el The element to listen on
14004  * @param {Object} config
14005  **/
14006 Roo.util.ClickRepeater = function(el, config)
14007 {
14008     this.el = Roo.get(el);
14009     this.el.unselectable();
14010
14011     Roo.apply(this, config);
14012
14013     this.addEvents({
14014     /**
14015      * @event mousedown
14016      * Fires when the mouse button is depressed.
14017      * @param {Roo.util.ClickRepeater} this
14018      */
14019         "mousedown" : true,
14020     /**
14021      * @event click
14022      * Fires on a specified interval during the time the element is pressed.
14023      * @param {Roo.util.ClickRepeater} this
14024      */
14025         "click" : true,
14026     /**
14027      * @event mouseup
14028      * Fires when the mouse key is released.
14029      * @param {Roo.util.ClickRepeater} this
14030      */
14031         "mouseup" : true
14032     });
14033
14034     this.el.on("mousedown", this.handleMouseDown, this);
14035     if(this.preventDefault || this.stopDefault){
14036         this.el.on("click", function(e){
14037             if(this.preventDefault){
14038                 e.preventDefault();
14039             }
14040             if(this.stopDefault){
14041                 e.stopEvent();
14042             }
14043         }, this);
14044     }
14045
14046     // allow inline handler
14047     if(this.handler){
14048         this.on("click", this.handler,  this.scope || this);
14049     }
14050
14051     Roo.util.ClickRepeater.superclass.constructor.call(this);
14052 };
14053
14054 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14055     interval : 20,
14056     delay: 250,
14057     preventDefault : true,
14058     stopDefault : false,
14059     timer : 0,
14060
14061     // private
14062     handleMouseDown : function(){
14063         clearTimeout(this.timer);
14064         this.el.blur();
14065         if(this.pressClass){
14066             this.el.addClass(this.pressClass);
14067         }
14068         this.mousedownTime = new Date();
14069
14070         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14071         this.el.on("mouseout", this.handleMouseOut, this);
14072
14073         this.fireEvent("mousedown", this);
14074         this.fireEvent("click", this);
14075         
14076         this.timer = this.click.defer(this.delay || this.interval, this);
14077     },
14078
14079     // private
14080     click : function(){
14081         this.fireEvent("click", this);
14082         this.timer = this.click.defer(this.getInterval(), this);
14083     },
14084
14085     // private
14086     getInterval: function(){
14087         if(!this.accelerate){
14088             return this.interval;
14089         }
14090         var pressTime = this.mousedownTime.getElapsed();
14091         if(pressTime < 500){
14092             return 400;
14093         }else if(pressTime < 1700){
14094             return 320;
14095         }else if(pressTime < 2600){
14096             return 250;
14097         }else if(pressTime < 3500){
14098             return 180;
14099         }else if(pressTime < 4400){
14100             return 140;
14101         }else if(pressTime < 5300){
14102             return 80;
14103         }else if(pressTime < 6200){
14104             return 50;
14105         }else{
14106             return 10;
14107         }
14108     },
14109
14110     // private
14111     handleMouseOut : function(){
14112         clearTimeout(this.timer);
14113         if(this.pressClass){
14114             this.el.removeClass(this.pressClass);
14115         }
14116         this.el.on("mouseover", this.handleMouseReturn, this);
14117     },
14118
14119     // private
14120     handleMouseReturn : function(){
14121         this.el.un("mouseover", this.handleMouseReturn);
14122         if(this.pressClass){
14123             this.el.addClass(this.pressClass);
14124         }
14125         this.click();
14126     },
14127
14128     // private
14129     handleMouseUp : function(){
14130         clearTimeout(this.timer);
14131         this.el.un("mouseover", this.handleMouseReturn);
14132         this.el.un("mouseout", this.handleMouseOut);
14133         Roo.get(document).un("mouseup", this.handleMouseUp);
14134         this.el.removeClass(this.pressClass);
14135         this.fireEvent("mouseup", this);
14136     }
14137 });/*
14138  * Based on:
14139  * Ext JS Library 1.1.1
14140  * Copyright(c) 2006-2007, Ext JS, LLC.
14141  *
14142  * Originally Released Under LGPL - original licence link has changed is not relivant.
14143  *
14144  * Fork - LGPL
14145  * <script type="text/javascript">
14146  */
14147
14148  
14149 /**
14150  * @class Roo.KeyNav
14151  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14152  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14153  * way to implement custom navigation schemes for any UI component.</p>
14154  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14155  * pageUp, pageDown, del, home, end.  Usage:</p>
14156  <pre><code>
14157 var nav = new Roo.KeyNav("my-element", {
14158     "left" : function(e){
14159         this.moveLeft(e.ctrlKey);
14160     },
14161     "right" : function(e){
14162         this.moveRight(e.ctrlKey);
14163     },
14164     "enter" : function(e){
14165         this.save();
14166     },
14167     scope : this
14168 });
14169 </code></pre>
14170  * @constructor
14171  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14172  * @param {Object} config The config
14173  */
14174 Roo.KeyNav = function(el, config){
14175     this.el = Roo.get(el);
14176     Roo.apply(this, config);
14177     if(!this.disabled){
14178         this.disabled = true;
14179         this.enable();
14180     }
14181 };
14182
14183 Roo.KeyNav.prototype = {
14184     /**
14185      * @cfg {Boolean} disabled
14186      * True to disable this KeyNav instance (defaults to false)
14187      */
14188     disabled : false,
14189     /**
14190      * @cfg {String} defaultEventAction
14191      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14192      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14193      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14194      */
14195     defaultEventAction: "stopEvent",
14196     /**
14197      * @cfg {Boolean} forceKeyDown
14198      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14199      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14200      * handle keydown instead of keypress.
14201      */
14202     forceKeyDown : false,
14203
14204     // private
14205     prepareEvent : function(e){
14206         var k = e.getKey();
14207         var h = this.keyToHandler[k];
14208         //if(h && this[h]){
14209         //    e.stopPropagation();
14210         //}
14211         if(Roo.isSafari && h && k >= 37 && k <= 40){
14212             e.stopEvent();
14213         }
14214     },
14215
14216     // private
14217     relay : function(e){
14218         var k = e.getKey();
14219         var h = this.keyToHandler[k];
14220         if(h && this[h]){
14221             if(this.doRelay(e, this[h], h) !== true){
14222                 e[this.defaultEventAction]();
14223             }
14224         }
14225     },
14226
14227     // private
14228     doRelay : function(e, h, hname){
14229         return h.call(this.scope || this, e);
14230     },
14231
14232     // possible handlers
14233     enter : false,
14234     left : false,
14235     right : false,
14236     up : false,
14237     down : false,
14238     tab : false,
14239     esc : false,
14240     pageUp : false,
14241     pageDown : false,
14242     del : false,
14243     home : false,
14244     end : false,
14245
14246     // quick lookup hash
14247     keyToHandler : {
14248         37 : "left",
14249         39 : "right",
14250         38 : "up",
14251         40 : "down",
14252         33 : "pageUp",
14253         34 : "pageDown",
14254         46 : "del",
14255         36 : "home",
14256         35 : "end",
14257         13 : "enter",
14258         27 : "esc",
14259         9  : "tab"
14260     },
14261
14262         /**
14263          * Enable this KeyNav
14264          */
14265         enable: function(){
14266                 if(this.disabled){
14267             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14268             // the EventObject will normalize Safari automatically
14269             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14270                 this.el.on("keydown", this.relay,  this);
14271             }else{
14272                 this.el.on("keydown", this.prepareEvent,  this);
14273                 this.el.on("keypress", this.relay,  this);
14274             }
14275                     this.disabled = false;
14276                 }
14277         },
14278
14279         /**
14280          * Disable this KeyNav
14281          */
14282         disable: function(){
14283                 if(!this.disabled){
14284                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14285                 this.el.un("keydown", this.relay);
14286             }else{
14287                 this.el.un("keydown", this.prepareEvent);
14288                 this.el.un("keypress", this.relay);
14289             }
14290                     this.disabled = true;
14291                 }
14292         }
14293 };/*
14294  * Based on:
14295  * Ext JS Library 1.1.1
14296  * Copyright(c) 2006-2007, Ext JS, LLC.
14297  *
14298  * Originally Released Under LGPL - original licence link has changed is not relivant.
14299  *
14300  * Fork - LGPL
14301  * <script type="text/javascript">
14302  */
14303
14304  
14305 /**
14306  * @class Roo.KeyMap
14307  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14308  * The constructor accepts the same config object as defined by {@link #addBinding}.
14309  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14310  * combination it will call the function with this signature (if the match is a multi-key
14311  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14312  * A KeyMap can also handle a string representation of keys.<br />
14313  * Usage:
14314  <pre><code>
14315 // map one key by key code
14316 var map = new Roo.KeyMap("my-element", {
14317     key: 13, // or Roo.EventObject.ENTER
14318     fn: myHandler,
14319     scope: myObject
14320 });
14321
14322 // map multiple keys to one action by string
14323 var map = new Roo.KeyMap("my-element", {
14324     key: "a\r\n\t",
14325     fn: myHandler,
14326     scope: myObject
14327 });
14328
14329 // map multiple keys to multiple actions by strings and array of codes
14330 var map = new Roo.KeyMap("my-element", [
14331     {
14332         key: [10,13],
14333         fn: function(){ alert("Return was pressed"); }
14334     }, {
14335         key: "abc",
14336         fn: function(){ alert('a, b or c was pressed'); }
14337     }, {
14338         key: "\t",
14339         ctrl:true,
14340         shift:true,
14341         fn: function(){ alert('Control + shift + tab was pressed.'); }
14342     }
14343 ]);
14344 </code></pre>
14345  * <b>Note: A KeyMap starts enabled</b>
14346  * @constructor
14347  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14348  * @param {Object} config The config (see {@link #addBinding})
14349  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14350  */
14351 Roo.KeyMap = function(el, config, eventName){
14352     this.el  = Roo.get(el);
14353     this.eventName = eventName || "keydown";
14354     this.bindings = [];
14355     if(config){
14356         this.addBinding(config);
14357     }
14358     this.enable();
14359 };
14360
14361 Roo.KeyMap.prototype = {
14362     /**
14363      * True to stop the event from bubbling and prevent the default browser action if the
14364      * key was handled by the KeyMap (defaults to false)
14365      * @type Boolean
14366      */
14367     stopEvent : false,
14368
14369     /**
14370      * Add a new binding to this KeyMap. The following config object properties are supported:
14371      * <pre>
14372 Property    Type             Description
14373 ----------  ---------------  ----------------------------------------------------------------------
14374 key         String/Array     A single keycode or an array of keycodes to handle
14375 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14376 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14377 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14378 fn          Function         The function to call when KeyMap finds the expected key combination
14379 scope       Object           The scope of the callback function
14380 </pre>
14381      *
14382      * Usage:
14383      * <pre><code>
14384 // Create a KeyMap
14385 var map = new Roo.KeyMap(document, {
14386     key: Roo.EventObject.ENTER,
14387     fn: handleKey,
14388     scope: this
14389 });
14390
14391 //Add a new binding to the existing KeyMap later
14392 map.addBinding({
14393     key: 'abc',
14394     shift: true,
14395     fn: handleKey,
14396     scope: this
14397 });
14398 </code></pre>
14399      * @param {Object/Array} config A single KeyMap config or an array of configs
14400      */
14401         addBinding : function(config){
14402         if(config instanceof Array){
14403             for(var i = 0, len = config.length; i < len; i++){
14404                 this.addBinding(config[i]);
14405             }
14406             return;
14407         }
14408         var keyCode = config.key,
14409             shift = config.shift, 
14410             ctrl = config.ctrl, 
14411             alt = config.alt,
14412             fn = config.fn,
14413             scope = config.scope;
14414         if(typeof keyCode == "string"){
14415             var ks = [];
14416             var keyString = keyCode.toUpperCase();
14417             for(var j = 0, len = keyString.length; j < len; j++){
14418                 ks.push(keyString.charCodeAt(j));
14419             }
14420             keyCode = ks;
14421         }
14422         var keyArray = keyCode instanceof Array;
14423         var handler = function(e){
14424             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14425                 var k = e.getKey();
14426                 if(keyArray){
14427                     for(var i = 0, len = keyCode.length; i < len; i++){
14428                         if(keyCode[i] == k){
14429                           if(this.stopEvent){
14430                               e.stopEvent();
14431                           }
14432                           fn.call(scope || window, k, e);
14433                           return;
14434                         }
14435                     }
14436                 }else{
14437                     if(k == keyCode){
14438                         if(this.stopEvent){
14439                            e.stopEvent();
14440                         }
14441                         fn.call(scope || window, k, e);
14442                     }
14443                 }
14444             }
14445         };
14446         this.bindings.push(handler);  
14447         },
14448
14449     /**
14450      * Shorthand for adding a single key listener
14451      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14452      * following options:
14453      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14454      * @param {Function} fn The function to call
14455      * @param {Object} scope (optional) The scope of the function
14456      */
14457     on : function(key, fn, scope){
14458         var keyCode, shift, ctrl, alt;
14459         if(typeof key == "object" && !(key instanceof Array)){
14460             keyCode = key.key;
14461             shift = key.shift;
14462             ctrl = key.ctrl;
14463             alt = key.alt;
14464         }else{
14465             keyCode = key;
14466         }
14467         this.addBinding({
14468             key: keyCode,
14469             shift: shift,
14470             ctrl: ctrl,
14471             alt: alt,
14472             fn: fn,
14473             scope: scope
14474         })
14475     },
14476
14477     // private
14478     handleKeyDown : function(e){
14479             if(this.enabled){ //just in case
14480             var b = this.bindings;
14481             for(var i = 0, len = b.length; i < len; i++){
14482                 b[i].call(this, e);
14483             }
14484             }
14485         },
14486         
14487         /**
14488          * Returns true if this KeyMap is enabled
14489          * @return {Boolean} 
14490          */
14491         isEnabled : function(){
14492             return this.enabled;  
14493         },
14494         
14495         /**
14496          * Enables this KeyMap
14497          */
14498         enable: function(){
14499                 if(!this.enabled){
14500                     this.el.on(this.eventName, this.handleKeyDown, this);
14501                     this.enabled = true;
14502                 }
14503         },
14504
14505         /**
14506          * Disable this KeyMap
14507          */
14508         disable: function(){
14509                 if(this.enabled){
14510                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14511                     this.enabled = false;
14512                 }
14513         }
14514 };/*
14515  * Based on:
14516  * Ext JS Library 1.1.1
14517  * Copyright(c) 2006-2007, Ext JS, LLC.
14518  *
14519  * Originally Released Under LGPL - original licence link has changed is not relivant.
14520  *
14521  * Fork - LGPL
14522  * <script type="text/javascript">
14523  */
14524
14525  
14526 /**
14527  * @class Roo.util.TextMetrics
14528  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14529  * wide, in pixels, a given block of text will be.
14530  * @singleton
14531  */
14532 Roo.util.TextMetrics = function(){
14533     var shared;
14534     return {
14535         /**
14536          * Measures the size of the specified text
14537          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14538          * that can affect the size of the rendered text
14539          * @param {String} text The text to measure
14540          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14541          * in order to accurately measure the text height
14542          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14543          */
14544         measure : function(el, text, fixedWidth){
14545             if(!shared){
14546                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14547             }
14548             shared.bind(el);
14549             shared.setFixedWidth(fixedWidth || 'auto');
14550             return shared.getSize(text);
14551         },
14552
14553         /**
14554          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14555          * the overhead of multiple calls to initialize the style properties on each measurement.
14556          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14557          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14558          * in order to accurately measure the text height
14559          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14560          */
14561         createInstance : function(el, fixedWidth){
14562             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14563         }
14564     };
14565 }();
14566
14567  
14568
14569 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14570     var ml = new Roo.Element(document.createElement('div'));
14571     document.body.appendChild(ml.dom);
14572     ml.position('absolute');
14573     ml.setLeftTop(-1000, -1000);
14574     ml.hide();
14575
14576     if(fixedWidth){
14577         ml.setWidth(fixedWidth);
14578     }
14579      
14580     var instance = {
14581         /**
14582          * Returns the size of the specified text based on the internal element's style and width properties
14583          * @memberOf Roo.util.TextMetrics.Instance#
14584          * @param {String} text The text to measure
14585          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14586          */
14587         getSize : function(text){
14588             ml.update(text);
14589             var s = ml.getSize();
14590             ml.update('');
14591             return s;
14592         },
14593
14594         /**
14595          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14596          * that can affect the size of the rendered text
14597          * @memberOf Roo.util.TextMetrics.Instance#
14598          * @param {String/HTMLElement} el The element, dom node or id
14599          */
14600         bind : function(el){
14601             ml.setStyle(
14602                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14603             );
14604         },
14605
14606         /**
14607          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14608          * to set a fixed width in order to accurately measure the text height.
14609          * @memberOf Roo.util.TextMetrics.Instance#
14610          * @param {Number} width The width to set on the element
14611          */
14612         setFixedWidth : function(width){
14613             ml.setWidth(width);
14614         },
14615
14616         /**
14617          * Returns the measured width of the specified text
14618          * @memberOf Roo.util.TextMetrics.Instance#
14619          * @param {String} text The text to measure
14620          * @return {Number} width The width in pixels
14621          */
14622         getWidth : function(text){
14623             ml.dom.style.width = 'auto';
14624             return this.getSize(text).width;
14625         },
14626
14627         /**
14628          * Returns the measured height of the specified text.  For multiline text, be sure to call
14629          * {@link #setFixedWidth} if necessary.
14630          * @memberOf Roo.util.TextMetrics.Instance#
14631          * @param {String} text The text to measure
14632          * @return {Number} height The height in pixels
14633          */
14634         getHeight : function(text){
14635             return this.getSize(text).height;
14636         }
14637     };
14638
14639     instance.bind(bindTo);
14640
14641     return instance;
14642 };
14643
14644 // backwards compat
14645 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14646  * Based on:
14647  * Ext JS Library 1.1.1
14648  * Copyright(c) 2006-2007, Ext JS, LLC.
14649  *
14650  * Originally Released Under LGPL - original licence link has changed is not relivant.
14651  *
14652  * Fork - LGPL
14653  * <script type="text/javascript">
14654  */
14655
14656 /**
14657  * @class Roo.state.Provider
14658  * Abstract base class for state provider implementations. This class provides methods
14659  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14660  * Provider interface.
14661  */
14662 Roo.state.Provider = function(){
14663     /**
14664      * @event statechange
14665      * Fires when a state change occurs.
14666      * @param {Provider} this This state provider
14667      * @param {String} key The state key which was changed
14668      * @param {String} value The encoded value for the state
14669      */
14670     this.addEvents({
14671         "statechange": true
14672     });
14673     this.state = {};
14674     Roo.state.Provider.superclass.constructor.call(this);
14675 };
14676 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14677     /**
14678      * Returns the current value for a key
14679      * @param {String} name The key name
14680      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14681      * @return {Mixed} The state data
14682      */
14683     get : function(name, defaultValue){
14684         return typeof this.state[name] == "undefined" ?
14685             defaultValue : this.state[name];
14686     },
14687     
14688     /**
14689      * Clears a value from the state
14690      * @param {String} name The key name
14691      */
14692     clear : function(name){
14693         delete this.state[name];
14694         this.fireEvent("statechange", this, name, null);
14695     },
14696     
14697     /**
14698      * Sets the value for a key
14699      * @param {String} name The key name
14700      * @param {Mixed} value The value to set
14701      */
14702     set : function(name, value){
14703         this.state[name] = value;
14704         this.fireEvent("statechange", this, name, value);
14705     },
14706     
14707     /**
14708      * Decodes a string previously encoded with {@link #encodeValue}.
14709      * @param {String} value The value to decode
14710      * @return {Mixed} The decoded value
14711      */
14712     decodeValue : function(cookie){
14713         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14714         var matches = re.exec(unescape(cookie));
14715         if(!matches || !matches[1]) return; // non state cookie
14716         var type = matches[1];
14717         var v = matches[2];
14718         switch(type){
14719             case "n":
14720                 return parseFloat(v);
14721             case "d":
14722                 return new Date(Date.parse(v));
14723             case "b":
14724                 return (v == "1");
14725             case "a":
14726                 var all = [];
14727                 var values = v.split("^");
14728                 for(var i = 0, len = values.length; i < len; i++){
14729                     all.push(this.decodeValue(values[i]));
14730                 }
14731                 return all;
14732            case "o":
14733                 var all = {};
14734                 var values = v.split("^");
14735                 for(var i = 0, len = values.length; i < len; i++){
14736                     var kv = values[i].split("=");
14737                     all[kv[0]] = this.decodeValue(kv[1]);
14738                 }
14739                 return all;
14740            default:
14741                 return v;
14742         }
14743     },
14744     
14745     /**
14746      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14747      * @param {Mixed} value The value to encode
14748      * @return {String} The encoded value
14749      */
14750     encodeValue : function(v){
14751         var enc;
14752         if(typeof v == "number"){
14753             enc = "n:" + v;
14754         }else if(typeof v == "boolean"){
14755             enc = "b:" + (v ? "1" : "0");
14756         }else if(v instanceof Date){
14757             enc = "d:" + v.toGMTString();
14758         }else if(v instanceof Array){
14759             var flat = "";
14760             for(var i = 0, len = v.length; i < len; i++){
14761                 flat += this.encodeValue(v[i]);
14762                 if(i != len-1) flat += "^";
14763             }
14764             enc = "a:" + flat;
14765         }else if(typeof v == "object"){
14766             var flat = "";
14767             for(var key in v){
14768                 if(typeof v[key] != "function"){
14769                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14770                 }
14771             }
14772             enc = "o:" + flat.substring(0, flat.length-1);
14773         }else{
14774             enc = "s:" + v;
14775         }
14776         return escape(enc);        
14777     }
14778 });
14779
14780 /*
14781  * Based on:
14782  * Ext JS Library 1.1.1
14783  * Copyright(c) 2006-2007, Ext JS, LLC.
14784  *
14785  * Originally Released Under LGPL - original licence link has changed is not relivant.
14786  *
14787  * Fork - LGPL
14788  * <script type="text/javascript">
14789  */
14790 /**
14791  * @class Roo.state.Manager
14792  * This is the global state manager. By default all components that are "state aware" check this class
14793  * for state information if you don't pass them a custom state provider. In order for this class
14794  * to be useful, it must be initialized with a provider when your application initializes.
14795  <pre><code>
14796 // in your initialization function
14797 init : function(){
14798    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14799    ...
14800    // supposed you have a {@link Roo.BorderLayout}
14801    var layout = new Roo.BorderLayout(...);
14802    layout.restoreState();
14803    // or a {Roo.BasicDialog}
14804    var dialog = new Roo.BasicDialog(...);
14805    dialog.restoreState();
14806  </code></pre>
14807  * @singleton
14808  */
14809 Roo.state.Manager = function(){
14810     var provider = new Roo.state.Provider();
14811     
14812     return {
14813         /**
14814          * Configures the default state provider for your application
14815          * @param {Provider} stateProvider The state provider to set
14816          */
14817         setProvider : function(stateProvider){
14818             provider = stateProvider;
14819         },
14820         
14821         /**
14822          * Returns the current value for a key
14823          * @param {String} name The key name
14824          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14825          * @return {Mixed} The state data
14826          */
14827         get : function(key, defaultValue){
14828             return provider.get(key, defaultValue);
14829         },
14830         
14831         /**
14832          * Sets the value for a key
14833          * @param {String} name The key name
14834          * @param {Mixed} value The state data
14835          */
14836          set : function(key, value){
14837             provider.set(key, value);
14838         },
14839         
14840         /**
14841          * Clears a value from the state
14842          * @param {String} name The key name
14843          */
14844         clear : function(key){
14845             provider.clear(key);
14846         },
14847         
14848         /**
14849          * Gets the currently configured state provider
14850          * @return {Provider} The state provider
14851          */
14852         getProvider : function(){
14853             return provider;
14854         }
14855     };
14856 }();
14857 /*
14858  * Based on:
14859  * Ext JS Library 1.1.1
14860  * Copyright(c) 2006-2007, Ext JS, LLC.
14861  *
14862  * Originally Released Under LGPL - original licence link has changed is not relivant.
14863  *
14864  * Fork - LGPL
14865  * <script type="text/javascript">
14866  */
14867 /**
14868  * @class Roo.state.CookieProvider
14869  * @extends Roo.state.Provider
14870  * The default Provider implementation which saves state via cookies.
14871  * <br />Usage:
14872  <pre><code>
14873    var cp = new Roo.state.CookieProvider({
14874        path: "/cgi-bin/",
14875        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14876        domain: "roojs.com"
14877    })
14878    Roo.state.Manager.setProvider(cp);
14879  </code></pre>
14880  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14881  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14882  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14883  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14884  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14885  * domain the page is running on including the 'www' like 'www.roojs.com')
14886  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14887  * @constructor
14888  * Create a new CookieProvider
14889  * @param {Object} config The configuration object
14890  */
14891 Roo.state.CookieProvider = function(config){
14892     Roo.state.CookieProvider.superclass.constructor.call(this);
14893     this.path = "/";
14894     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14895     this.domain = null;
14896     this.secure = false;
14897     Roo.apply(this, config);
14898     this.state = this.readCookies();
14899 };
14900
14901 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14902     // private
14903     set : function(name, value){
14904         if(typeof value == "undefined" || value === null){
14905             this.clear(name);
14906             return;
14907         }
14908         this.setCookie(name, value);
14909         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14910     },
14911
14912     // private
14913     clear : function(name){
14914         this.clearCookie(name);
14915         Roo.state.CookieProvider.superclass.clear.call(this, name);
14916     },
14917
14918     // private
14919     readCookies : function(){
14920         var cookies = {};
14921         var c = document.cookie + ";";
14922         var re = /\s?(.*?)=(.*?);/g;
14923         var matches;
14924         while((matches = re.exec(c)) != null){
14925             var name = matches[1];
14926             var value = matches[2];
14927             if(name && name.substring(0,3) == "ys-"){
14928                 cookies[name.substr(3)] = this.decodeValue(value);
14929             }
14930         }
14931         return cookies;
14932     },
14933
14934     // private
14935     setCookie : function(name, value){
14936         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14937            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14938            ((this.path == null) ? "" : ("; path=" + this.path)) +
14939            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14940            ((this.secure == true) ? "; secure" : "");
14941     },
14942
14943     // private
14944     clearCookie : function(name){
14945         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14946            ((this.path == null) ? "" : ("; path=" + this.path)) +
14947            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14948            ((this.secure == true) ? "; secure" : "");
14949     }
14950 });/*
14951  * Based on:
14952  * Ext JS Library 1.1.1
14953  * Copyright(c) 2006-2007, Ext JS, LLC.
14954  *
14955  * Originally Released Under LGPL - original licence link has changed is not relivant.
14956  *
14957  * Fork - LGPL
14958  * <script type="text/javascript">
14959  */
14960
14961
14962
14963 /*
14964  * These classes are derivatives of the similarly named classes in the YUI Library.
14965  * The original license:
14966  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14967  * Code licensed under the BSD License:
14968  * http://developer.yahoo.net/yui/license.txt
14969  */
14970
14971 (function() {
14972
14973 var Event=Roo.EventManager;
14974 var Dom=Roo.lib.Dom;
14975
14976 /**
14977  * @class Roo.dd.DragDrop
14978  * @extends Roo.util.Observable
14979  * Defines the interface and base operation of items that that can be
14980  * dragged or can be drop targets.  It was designed to be extended, overriding
14981  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14982  * Up to three html elements can be associated with a DragDrop instance:
14983  * <ul>
14984  * <li>linked element: the element that is passed into the constructor.
14985  * This is the element which defines the boundaries for interaction with
14986  * other DragDrop objects.</li>
14987  * <li>handle element(s): The drag operation only occurs if the element that
14988  * was clicked matches a handle element.  By default this is the linked
14989  * element, but there are times that you will want only a portion of the
14990  * linked element to initiate the drag operation, and the setHandleElId()
14991  * method provides a way to define this.</li>
14992  * <li>drag element: this represents the element that would be moved along
14993  * with the cursor during a drag operation.  By default, this is the linked
14994  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14995  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14996  * </li>
14997  * </ul>
14998  * This class should not be instantiated until the onload event to ensure that
14999  * the associated elements are available.
15000  * The following would define a DragDrop obj that would interact with any
15001  * other DragDrop obj in the "group1" group:
15002  * <pre>
15003  *  dd = new Roo.dd.DragDrop("div1", "group1");
15004  * </pre>
15005  * Since none of the event handlers have been implemented, nothing would
15006  * actually happen if you were to run the code above.  Normally you would
15007  * override this class or one of the default implementations, but you can
15008  * also override the methods you want on an instance of the class...
15009  * <pre>
15010  *  dd.onDragDrop = function(e, id) {
15011  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
15012  *  }
15013  * </pre>
15014  * @constructor
15015  * @param {String} id of the element that is linked to this instance
15016  * @param {String} sGroup the group of related DragDrop objects
15017  * @param {object} config an object containing configurable attributes
15018  *                Valid properties for DragDrop:
15019  *                    padding, isTarget, maintainOffset, primaryButtonOnly
15020  */
15021 Roo.dd.DragDrop = function(id, sGroup, config) {
15022     if (id) {
15023         this.init(id, sGroup, config);
15024     }
15025     
15026 };
15027
15028 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
15029
15030     /**
15031      * The id of the element associated with this object.  This is what we
15032      * refer to as the "linked element" because the size and position of
15033      * this element is used to determine when the drag and drop objects have
15034      * interacted.
15035      * @property id
15036      * @type String
15037      */
15038     id: null,
15039
15040     /**
15041      * Configuration attributes passed into the constructor
15042      * @property config
15043      * @type object
15044      */
15045     config: null,
15046
15047     /**
15048      * The id of the element that will be dragged.  By default this is same
15049      * as the linked element , but could be changed to another element. Ex:
15050      * Roo.dd.DDProxy
15051      * @property dragElId
15052      * @type String
15053      * @private
15054      */
15055     dragElId: null,
15056
15057     /**
15058      * the id of the element that initiates the drag operation.  By default
15059      * this is the linked element, but could be changed to be a child of this
15060      * element.  This lets us do things like only starting the drag when the
15061      * header element within the linked html element is clicked.
15062      * @property handleElId
15063      * @type String
15064      * @private
15065      */
15066     handleElId: null,
15067
15068     /**
15069      * An associative array of HTML tags that will be ignored if clicked.
15070      * @property invalidHandleTypes
15071      * @type {string: string}
15072      */
15073     invalidHandleTypes: null,
15074
15075     /**
15076      * An associative array of ids for elements that will be ignored if clicked
15077      * @property invalidHandleIds
15078      * @type {string: string}
15079      */
15080     invalidHandleIds: null,
15081
15082     /**
15083      * An indexted array of css class names for elements that will be ignored
15084      * if clicked.
15085      * @property invalidHandleClasses
15086      * @type string[]
15087      */
15088     invalidHandleClasses: null,
15089
15090     /**
15091      * The linked element's absolute X position at the time the drag was
15092      * started
15093      * @property startPageX
15094      * @type int
15095      * @private
15096      */
15097     startPageX: 0,
15098
15099     /**
15100      * The linked element's absolute X position at the time the drag was
15101      * started
15102      * @property startPageY
15103      * @type int
15104      * @private
15105      */
15106     startPageY: 0,
15107
15108     /**
15109      * The group defines a logical collection of DragDrop objects that are
15110      * related.  Instances only get events when interacting with other
15111      * DragDrop object in the same group.  This lets us define multiple
15112      * groups using a single DragDrop subclass if we want.
15113      * @property groups
15114      * @type {string: string}
15115      */
15116     groups: null,
15117
15118     /**
15119      * Individual drag/drop instances can be locked.  This will prevent
15120      * onmousedown start drag.
15121      * @property locked
15122      * @type boolean
15123      * @private
15124      */
15125     locked: false,
15126
15127     /**
15128      * Lock this instance
15129      * @method lock
15130      */
15131     lock: function() { this.locked = true; },
15132
15133     /**
15134      * Unlock this instace
15135      * @method unlock
15136      */
15137     unlock: function() { this.locked = false; },
15138
15139     /**
15140      * By default, all insances can be a drop target.  This can be disabled by
15141      * setting isTarget to false.
15142      * @method isTarget
15143      * @type boolean
15144      */
15145     isTarget: true,
15146
15147     /**
15148      * The padding configured for this drag and drop object for calculating
15149      * the drop zone intersection with this object.
15150      * @method padding
15151      * @type int[]
15152      */
15153     padding: null,
15154
15155     /**
15156      * Cached reference to the linked element
15157      * @property _domRef
15158      * @private
15159      */
15160     _domRef: null,
15161
15162     /**
15163      * Internal typeof flag
15164      * @property __ygDragDrop
15165      * @private
15166      */
15167     __ygDragDrop: true,
15168
15169     /**
15170      * Set to true when horizontal contraints are applied
15171      * @property constrainX
15172      * @type boolean
15173      * @private
15174      */
15175     constrainX: false,
15176
15177     /**
15178      * Set to true when vertical contraints are applied
15179      * @property constrainY
15180      * @type boolean
15181      * @private
15182      */
15183     constrainY: false,
15184
15185     /**
15186      * The left constraint
15187      * @property minX
15188      * @type int
15189      * @private
15190      */
15191     minX: 0,
15192
15193     /**
15194      * The right constraint
15195      * @property maxX
15196      * @type int
15197      * @private
15198      */
15199     maxX: 0,
15200
15201     /**
15202      * The up constraint
15203      * @property minY
15204      * @type int
15205      * @type int
15206      * @private
15207      */
15208     minY: 0,
15209
15210     /**
15211      * The down constraint
15212      * @property maxY
15213      * @type int
15214      * @private
15215      */
15216     maxY: 0,
15217
15218     /**
15219      * Maintain offsets when we resetconstraints.  Set to true when you want
15220      * the position of the element relative to its parent to stay the same
15221      * when the page changes
15222      *
15223      * @property maintainOffset
15224      * @type boolean
15225      */
15226     maintainOffset: false,
15227
15228     /**
15229      * Array of pixel locations the element will snap to if we specified a
15230      * horizontal graduation/interval.  This array is generated automatically
15231      * when you define a tick interval.
15232      * @property xTicks
15233      * @type int[]
15234      */
15235     xTicks: null,
15236
15237     /**
15238      * Array of pixel locations the element will snap to if we specified a
15239      * vertical graduation/interval.  This array is generated automatically
15240      * when you define a tick interval.
15241      * @property yTicks
15242      * @type int[]
15243      */
15244     yTicks: null,
15245
15246     /**
15247      * By default the drag and drop instance will only respond to the primary
15248      * button click (left button for a right-handed mouse).  Set to true to
15249      * allow drag and drop to start with any mouse click that is propogated
15250      * by the browser
15251      * @property primaryButtonOnly
15252      * @type boolean
15253      */
15254     primaryButtonOnly: true,
15255
15256     /**
15257      * The availabe property is false until the linked dom element is accessible.
15258      * @property available
15259      * @type boolean
15260      */
15261     available: false,
15262
15263     /**
15264      * By default, drags can only be initiated if the mousedown occurs in the
15265      * region the linked element is.  This is done in part to work around a
15266      * bug in some browsers that mis-report the mousedown if the previous
15267      * mouseup happened outside of the window.  This property is set to true
15268      * if outer handles are defined.
15269      *
15270      * @property hasOuterHandles
15271      * @type boolean
15272      * @default false
15273      */
15274     hasOuterHandles: false,
15275
15276     /**
15277      * Code that executes immediately before the startDrag event
15278      * @method b4StartDrag
15279      * @private
15280      */
15281     b4StartDrag: function(x, y) { },
15282
15283     /**
15284      * Abstract method called after a drag/drop object is clicked
15285      * and the drag or mousedown time thresholds have beeen met.
15286      * @method startDrag
15287      * @param {int} X click location
15288      * @param {int} Y click location
15289      */
15290     startDrag: function(x, y) { /* override this */ },
15291
15292     /**
15293      * Code that executes immediately before the onDrag event
15294      * @method b4Drag
15295      * @private
15296      */
15297     b4Drag: function(e) { },
15298
15299     /**
15300      * Abstract method called during the onMouseMove event while dragging an
15301      * object.
15302      * @method onDrag
15303      * @param {Event} e the mousemove event
15304      */
15305     onDrag: function(e) { /* override this */ },
15306
15307     /**
15308      * Abstract method called when this element fist begins hovering over
15309      * another DragDrop obj
15310      * @method onDragEnter
15311      * @param {Event} e the mousemove event
15312      * @param {String|DragDrop[]} id In POINT mode, the element
15313      * id this is hovering over.  In INTERSECT mode, an array of one or more
15314      * dragdrop items being hovered over.
15315      */
15316     onDragEnter: function(e, id) { /* override this */ },
15317
15318     /**
15319      * Code that executes immediately before the onDragOver event
15320      * @method b4DragOver
15321      * @private
15322      */
15323     b4DragOver: function(e) { },
15324
15325     /**
15326      * Abstract method called when this element is hovering over another
15327      * DragDrop obj
15328      * @method onDragOver
15329      * @param {Event} e the mousemove event
15330      * @param {String|DragDrop[]} id In POINT mode, the element
15331      * id this is hovering over.  In INTERSECT mode, an array of dd items
15332      * being hovered over.
15333      */
15334     onDragOver: function(e, id) { /* override this */ },
15335
15336     /**
15337      * Code that executes immediately before the onDragOut event
15338      * @method b4DragOut
15339      * @private
15340      */
15341     b4DragOut: function(e) { },
15342
15343     /**
15344      * Abstract method called when we are no longer hovering over an element
15345      * @method onDragOut
15346      * @param {Event} e the mousemove event
15347      * @param {String|DragDrop[]} id In POINT mode, the element
15348      * id this was hovering over.  In INTERSECT mode, an array of dd items
15349      * that the mouse is no longer over.
15350      */
15351     onDragOut: function(e, id) { /* override this */ },
15352
15353     /**
15354      * Code that executes immediately before the onDragDrop event
15355      * @method b4DragDrop
15356      * @private
15357      */
15358     b4DragDrop: function(e) { },
15359
15360     /**
15361      * Abstract method called when this item is dropped on another DragDrop
15362      * obj
15363      * @method onDragDrop
15364      * @param {Event} e the mouseup event
15365      * @param {String|DragDrop[]} id In POINT mode, the element
15366      * id this was dropped on.  In INTERSECT mode, an array of dd items this
15367      * was dropped on.
15368      */
15369     onDragDrop: function(e, id) { /* override this */ },
15370
15371     /**
15372      * Abstract method called when this item is dropped on an area with no
15373      * drop target
15374      * @method onInvalidDrop
15375      * @param {Event} e the mouseup event
15376      */
15377     onInvalidDrop: function(e) { /* override this */ },
15378
15379     /**
15380      * Code that executes immediately before the endDrag event
15381      * @method b4EndDrag
15382      * @private
15383      */
15384     b4EndDrag: function(e) { },
15385
15386     /**
15387      * Fired when we are done dragging the object
15388      * @method endDrag
15389      * @param {Event} e the mouseup event
15390      */
15391     endDrag: function(e) { /* override this */ },
15392
15393     /**
15394      * Code executed immediately before the onMouseDown event
15395      * @method b4MouseDown
15396      * @param {Event} e the mousedown event
15397      * @private
15398      */
15399     b4MouseDown: function(e) {  },
15400
15401     /**
15402      * Event handler that fires when a drag/drop obj gets a mousedown
15403      * @method onMouseDown
15404      * @param {Event} e the mousedown event
15405      */
15406     onMouseDown: function(e) { /* override this */ },
15407
15408     /**
15409      * Event handler that fires when a drag/drop obj gets a mouseup
15410      * @method onMouseUp
15411      * @param {Event} e the mouseup event
15412      */
15413     onMouseUp: function(e) { /* override this */ },
15414
15415     /**
15416      * Override the onAvailable method to do what is needed after the initial
15417      * position was determined.
15418      * @method onAvailable
15419      */
15420     onAvailable: function () {
15421     },
15422
15423     /*
15424      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
15425      * @type Object
15426      */
15427     defaultPadding : {left:0, right:0, top:0, bottom:0},
15428
15429     /*
15430      * Initializes the drag drop object's constraints to restrict movement to a certain element.
15431  *
15432  * Usage:
15433  <pre><code>
15434  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
15435                 { dragElId: "existingProxyDiv" });
15436  dd.startDrag = function(){
15437      this.constrainTo("parent-id");
15438  };
15439  </code></pre>
15440  * Or you can initalize it using the {@link Roo.Element} object:
15441  <pre><code>
15442  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
15443      startDrag : function(){
15444          this.constrainTo("parent-id");
15445      }
15446  });
15447  </code></pre>
15448      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
15449      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
15450      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
15451      * an object containing the sides to pad. For example: {right:10, bottom:10}
15452      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
15453      */
15454     constrainTo : function(constrainTo, pad, inContent){
15455         if(typeof pad == "number"){
15456             pad = {left: pad, right:pad, top:pad, bottom:pad};
15457         }
15458         pad = pad || this.defaultPadding;
15459         var b = Roo.get(this.getEl()).getBox();
15460         var ce = Roo.get(constrainTo);
15461         var s = ce.getScroll();
15462         var c, cd = ce.dom;
15463         if(cd == document.body){
15464             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15465         }else{
15466             xy = ce.getXY();
15467             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15468         }
15469
15470
15471         var topSpace = b.y - c.y;
15472         var leftSpace = b.x - c.x;
15473
15474         this.resetConstraints();
15475         this.setXConstraint(leftSpace - (pad.left||0), // left
15476                 c.width - leftSpace - b.width - (pad.right||0) //right
15477         );
15478         this.setYConstraint(topSpace - (pad.top||0), //top
15479                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15480         );
15481     },
15482
15483     /**
15484      * Returns a reference to the linked element
15485      * @method getEl
15486      * @return {HTMLElement} the html element
15487      */
15488     getEl: function() {
15489         if (!this._domRef) {
15490             this._domRef = Roo.getDom(this.id);
15491         }
15492
15493         return this._domRef;
15494     },
15495
15496     /**
15497      * Returns a reference to the actual element to drag.  By default this is
15498      * the same as the html element, but it can be assigned to another
15499      * element. An example of this can be found in Roo.dd.DDProxy
15500      * @method getDragEl
15501      * @return {HTMLElement} the html element
15502      */
15503     getDragEl: function() {
15504         return Roo.getDom(this.dragElId);
15505     },
15506
15507     /**
15508      * Sets up the DragDrop object.  Must be called in the constructor of any
15509      * Roo.dd.DragDrop subclass
15510      * @method init
15511      * @param id the id of the linked element
15512      * @param {String} sGroup the group of related items
15513      * @param {object} config configuration attributes
15514      */
15515     init: function(id, sGroup, config) {
15516         this.initTarget(id, sGroup, config);
15517         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15518         // Event.on(this.id, "selectstart", Event.preventDefault);
15519     },
15520
15521     /**
15522      * Initializes Targeting functionality only... the object does not
15523      * get a mousedown handler.
15524      * @method initTarget
15525      * @param id the id of the linked element
15526      * @param {String} sGroup the group of related items
15527      * @param {object} config configuration attributes
15528      */
15529     initTarget: function(id, sGroup, config) {
15530
15531         // configuration attributes
15532         this.config = config || {};
15533
15534         // create a local reference to the drag and drop manager
15535         this.DDM = Roo.dd.DDM;
15536         // initialize the groups array
15537         this.groups = {};
15538
15539         // assume that we have an element reference instead of an id if the
15540         // parameter is not a string
15541         if (typeof id !== "string") {
15542             id = Roo.id(id);
15543         }
15544
15545         // set the id
15546         this.id = id;
15547
15548         // add to an interaction group
15549         this.addToGroup((sGroup) ? sGroup : "default");
15550
15551         // We don't want to register this as the handle with the manager
15552         // so we just set the id rather than calling the setter.
15553         this.handleElId = id;
15554
15555         // the linked element is the element that gets dragged by default
15556         this.setDragElId(id);
15557
15558         // by default, clicked anchors will not start drag operations.
15559         this.invalidHandleTypes = { A: "A" };
15560         this.invalidHandleIds = {};
15561         this.invalidHandleClasses = [];
15562
15563         this.applyConfig();
15564
15565         this.handleOnAvailable();
15566     },
15567
15568     /**
15569      * Applies the configuration parameters that were passed into the constructor.
15570      * This is supposed to happen at each level through the inheritance chain.  So
15571      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15572      * DragDrop in order to get all of the parameters that are available in
15573      * each object.
15574      * @method applyConfig
15575      */
15576     applyConfig: function() {
15577
15578         // configurable properties:
15579         //    padding, isTarget, maintainOffset, primaryButtonOnly
15580         this.padding           = this.config.padding || [0, 0, 0, 0];
15581         this.isTarget          = (this.config.isTarget !== false);
15582         this.maintainOffset    = (this.config.maintainOffset);
15583         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15584
15585     },
15586
15587     /**
15588      * Executed when the linked element is available
15589      * @method handleOnAvailable
15590      * @private
15591      */
15592     handleOnAvailable: function() {
15593         this.available = true;
15594         this.resetConstraints();
15595         this.onAvailable();
15596     },
15597
15598      /**
15599      * Configures the padding for the target zone in px.  Effectively expands
15600      * (or reduces) the virtual object size for targeting calculations.
15601      * Supports css-style shorthand; if only one parameter is passed, all sides
15602      * will have that padding, and if only two are passed, the top and bottom
15603      * will have the first param, the left and right the second.
15604      * @method setPadding
15605      * @param {int} iTop    Top pad
15606      * @param {int} iRight  Right pad
15607      * @param {int} iBot    Bot pad
15608      * @param {int} iLeft   Left pad
15609      */
15610     setPadding: function(iTop, iRight, iBot, iLeft) {
15611         // this.padding = [iLeft, iRight, iTop, iBot];
15612         if (!iRight && 0 !== iRight) {
15613             this.padding = [iTop, iTop, iTop, iTop];
15614         } else if (!iBot && 0 !== iBot) {
15615             this.padding = [iTop, iRight, iTop, iRight];
15616         } else {
15617             this.padding = [iTop, iRight, iBot, iLeft];
15618         }
15619     },
15620
15621     /**
15622      * Stores the initial placement of the linked element.
15623      * @method setInitialPosition
15624      * @param {int} diffX   the X offset, default 0
15625      * @param {int} diffY   the Y offset, default 0
15626      */
15627     setInitPosition: function(diffX, diffY) {
15628         var el = this.getEl();
15629
15630         if (!this.DDM.verifyEl(el)) {
15631             return;
15632         }
15633
15634         var dx = diffX || 0;
15635         var dy = diffY || 0;
15636
15637         var p = Dom.getXY( el );
15638
15639         this.initPageX = p[0] - dx;
15640         this.initPageY = p[1] - dy;
15641
15642         this.lastPageX = p[0];
15643         this.lastPageY = p[1];
15644
15645
15646         this.setStartPosition(p);
15647     },
15648
15649     /**
15650      * Sets the start position of the element.  This is set when the obj
15651      * is initialized, the reset when a drag is started.
15652      * @method setStartPosition
15653      * @param pos current position (from previous lookup)
15654      * @private
15655      */
15656     setStartPosition: function(pos) {
15657         var p = pos || Dom.getXY( this.getEl() );
15658         this.deltaSetXY = null;
15659
15660         this.startPageX = p[0];
15661         this.startPageY = p[1];
15662     },
15663
15664     /**
15665      * Add this instance to a group of related drag/drop objects.  All
15666      * instances belong to at least one group, and can belong to as many
15667      * groups as needed.
15668      * @method addToGroup
15669      * @param sGroup {string} the name of the group
15670      */
15671     addToGroup: function(sGroup) {
15672         this.groups[sGroup] = true;
15673         this.DDM.regDragDrop(this, sGroup);
15674     },
15675
15676     /**
15677      * Remove's this instance from the supplied interaction group
15678      * @method removeFromGroup
15679      * @param {string}  sGroup  The group to drop
15680      */
15681     removeFromGroup: function(sGroup) {
15682         if (this.groups[sGroup]) {
15683             delete this.groups[sGroup];
15684         }
15685
15686         this.DDM.removeDDFromGroup(this, sGroup);
15687     },
15688
15689     /**
15690      * Allows you to specify that an element other than the linked element
15691      * will be moved with the cursor during a drag
15692      * @method setDragElId
15693      * @param id {string} the id of the element that will be used to initiate the drag
15694      */
15695     setDragElId: function(id) {
15696         this.dragElId = id;
15697     },
15698
15699     /**
15700      * Allows you to specify a child of the linked element that should be
15701      * used to initiate the drag operation.  An example of this would be if
15702      * you have a content div with text and links.  Clicking anywhere in the
15703      * content area would normally start the drag operation.  Use this method
15704      * to specify that an element inside of the content div is the element
15705      * that starts the drag operation.
15706      * @method setHandleElId
15707      * @param id {string} the id of the element that will be used to
15708      * initiate the drag.
15709      */
15710     setHandleElId: function(id) {
15711         if (typeof id !== "string") {
15712             id = Roo.id(id);
15713         }
15714         this.handleElId = id;
15715         this.DDM.regHandle(this.id, id);
15716     },
15717
15718     /**
15719      * Allows you to set an element outside of the linked element as a drag
15720      * handle
15721      * @method setOuterHandleElId
15722      * @param id the id of the element that will be used to initiate the drag
15723      */
15724     setOuterHandleElId: function(id) {
15725         if (typeof id !== "string") {
15726             id = Roo.id(id);
15727         }
15728         Event.on(id, "mousedown",
15729                 this.handleMouseDown, this);
15730         this.setHandleElId(id);
15731
15732         this.hasOuterHandles = true;
15733     },
15734
15735     /**
15736      * Remove all drag and drop hooks for this element
15737      * @method unreg
15738      */
15739     unreg: function() {
15740         Event.un(this.id, "mousedown",
15741                 this.handleMouseDown);
15742         this._domRef = null;
15743         this.DDM._remove(this);
15744     },
15745
15746     destroy : function(){
15747         this.unreg();
15748     },
15749
15750     /**
15751      * Returns true if this instance is locked, or the drag drop mgr is locked
15752      * (meaning that all drag/drop is disabled on the page.)
15753      * @method isLocked
15754      * @return {boolean} true if this obj or all drag/drop is locked, else
15755      * false
15756      */
15757     isLocked: function() {
15758         return (this.DDM.isLocked() || this.locked);
15759     },
15760
15761     /**
15762      * Fired when this object is clicked
15763      * @method handleMouseDown
15764      * @param {Event} e
15765      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15766      * @private
15767      */
15768     handleMouseDown: function(e, oDD){
15769         if (this.primaryButtonOnly && e.button != 0) {
15770             return;
15771         }
15772
15773         if (this.isLocked()) {
15774             return;
15775         }
15776
15777         this.DDM.refreshCache(this.groups);
15778
15779         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15780         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15781         } else {
15782             if (this.clickValidator(e)) {
15783
15784                 // set the initial element position
15785                 this.setStartPosition();
15786
15787
15788                 this.b4MouseDown(e);
15789                 this.onMouseDown(e);
15790
15791                 this.DDM.handleMouseDown(e, this);
15792
15793                 this.DDM.stopEvent(e);
15794             } else {
15795
15796
15797             }
15798         }
15799     },
15800
15801     clickValidator: function(e) {
15802         var target = e.getTarget();
15803         return ( this.isValidHandleChild(target) &&
15804                     (this.id == this.handleElId ||
15805                         this.DDM.handleWasClicked(target, this.id)) );
15806     },
15807
15808     /**
15809      * Allows you to specify a tag name that should not start a drag operation
15810      * when clicked.  This is designed to facilitate embedding links within a
15811      * drag handle that do something other than start the drag.
15812      * @method addInvalidHandleType
15813      * @param {string} tagName the type of element to exclude
15814      */
15815     addInvalidHandleType: function(tagName) {
15816         var type = tagName.toUpperCase();
15817         this.invalidHandleTypes[type] = type;
15818     },
15819
15820     /**
15821      * Lets you to specify an element id for a child of a drag handle
15822      * that should not initiate a drag
15823      * @method addInvalidHandleId
15824      * @param {string} id the element id of the element you wish to ignore
15825      */
15826     addInvalidHandleId: function(id) {
15827         if (typeof id !== "string") {
15828             id = Roo.id(id);
15829         }
15830         this.invalidHandleIds[id] = id;
15831     },
15832
15833     /**
15834      * Lets you specify a css class of elements that will not initiate a drag
15835      * @method addInvalidHandleClass
15836      * @param {string} cssClass the class of the elements you wish to ignore
15837      */
15838     addInvalidHandleClass: function(cssClass) {
15839         this.invalidHandleClasses.push(cssClass);
15840     },
15841
15842     /**
15843      * Unsets an excluded tag name set by addInvalidHandleType
15844      * @method removeInvalidHandleType
15845      * @param {string} tagName the type of element to unexclude
15846      */
15847     removeInvalidHandleType: function(tagName) {
15848         var type = tagName.toUpperCase();
15849         // this.invalidHandleTypes[type] = null;
15850         delete this.invalidHandleTypes[type];
15851     },
15852
15853     /**
15854      * Unsets an invalid handle id
15855      * @method removeInvalidHandleId
15856      * @param {string} id the id of the element to re-enable
15857      */
15858     removeInvalidHandleId: function(id) {
15859         if (typeof id !== "string") {
15860             id = Roo.id(id);
15861         }
15862         delete this.invalidHandleIds[id];
15863     },
15864
15865     /**
15866      * Unsets an invalid css class
15867      * @method removeInvalidHandleClass
15868      * @param {string} cssClass the class of the element(s) you wish to
15869      * re-enable
15870      */
15871     removeInvalidHandleClass: function(cssClass) {
15872         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15873             if (this.invalidHandleClasses[i] == cssClass) {
15874                 delete this.invalidHandleClasses[i];
15875             }
15876         }
15877     },
15878
15879     /**
15880      * Checks the tag exclusion list to see if this click should be ignored
15881      * @method isValidHandleChild
15882      * @param {HTMLElement} node the HTMLElement to evaluate
15883      * @return {boolean} true if this is a valid tag type, false if not
15884      */
15885     isValidHandleChild: function(node) {
15886
15887         var valid = true;
15888         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15889         var nodeName;
15890         try {
15891             nodeName = node.nodeName.toUpperCase();
15892         } catch(e) {
15893             nodeName = node.nodeName;
15894         }
15895         valid = valid && !this.invalidHandleTypes[nodeName];
15896         valid = valid && !this.invalidHandleIds[node.id];
15897
15898         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15899             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15900         }
15901
15902
15903         return valid;
15904
15905     },
15906
15907     /**
15908      * Create the array of horizontal tick marks if an interval was specified
15909      * in setXConstraint().
15910      * @method setXTicks
15911      * @private
15912      */
15913     setXTicks: function(iStartX, iTickSize) {
15914         this.xTicks = [];
15915         this.xTickSize = iTickSize;
15916
15917         var tickMap = {};
15918
15919         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15920             if (!tickMap[i]) {
15921                 this.xTicks[this.xTicks.length] = i;
15922                 tickMap[i] = true;
15923             }
15924         }
15925
15926         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15927             if (!tickMap[i]) {
15928                 this.xTicks[this.xTicks.length] = i;
15929                 tickMap[i] = true;
15930             }
15931         }
15932
15933         this.xTicks.sort(this.DDM.numericSort) ;
15934     },
15935
15936     /**
15937      * Create the array of vertical tick marks if an interval was specified in
15938      * setYConstraint().
15939      * @method setYTicks
15940      * @private
15941      */
15942     setYTicks: function(iStartY, iTickSize) {
15943         this.yTicks = [];
15944         this.yTickSize = iTickSize;
15945
15946         var tickMap = {};
15947
15948         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15949             if (!tickMap[i]) {
15950                 this.yTicks[this.yTicks.length] = i;
15951                 tickMap[i] = true;
15952             }
15953         }
15954
15955         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15956             if (!tickMap[i]) {
15957                 this.yTicks[this.yTicks.length] = i;
15958                 tickMap[i] = true;
15959             }
15960         }
15961
15962         this.yTicks.sort(this.DDM.numericSort) ;
15963     },
15964
15965     /**
15966      * By default, the element can be dragged any place on the screen.  Use
15967      * this method to limit the horizontal travel of the element.  Pass in
15968      * 0,0 for the parameters if you want to lock the drag to the y axis.
15969      * @method setXConstraint
15970      * @param {int} iLeft the number of pixels the element can move to the left
15971      * @param {int} iRight the number of pixels the element can move to the
15972      * right
15973      * @param {int} iTickSize optional parameter for specifying that the
15974      * element
15975      * should move iTickSize pixels at a time.
15976      */
15977     setXConstraint: function(iLeft, iRight, iTickSize) {
15978         this.leftConstraint = iLeft;
15979         this.rightConstraint = iRight;
15980
15981         this.minX = this.initPageX - iLeft;
15982         this.maxX = this.initPageX + iRight;
15983         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15984
15985         this.constrainX = true;
15986     },
15987
15988     /**
15989      * Clears any constraints applied to this instance.  Also clears ticks
15990      * since they can't exist independent of a constraint at this time.
15991      * @method clearConstraints
15992      */
15993     clearConstraints: function() {
15994         this.constrainX = false;
15995         this.constrainY = false;
15996         this.clearTicks();
15997     },
15998
15999     /**
16000      * Clears any tick interval defined for this instance
16001      * @method clearTicks
16002      */
16003     clearTicks: function() {
16004         this.xTicks = null;
16005         this.yTicks = null;
16006         this.xTickSize = 0;
16007         this.yTickSize = 0;
16008     },
16009
16010     /**
16011      * By default, the element can be dragged any place on the screen.  Set
16012      * this to limit the vertical travel of the element.  Pass in 0,0 for the
16013      * parameters if you want to lock the drag to the x axis.
16014      * @method setYConstraint
16015      * @param {int} iUp the number of pixels the element can move up
16016      * @param {int} iDown the number of pixels the element can move down
16017      * @param {int} iTickSize optional parameter for specifying that the
16018      * element should move iTickSize pixels at a time.
16019      */
16020     setYConstraint: function(iUp, iDown, iTickSize) {
16021         this.topConstraint = iUp;
16022         this.bottomConstraint = iDown;
16023
16024         this.minY = this.initPageY - iUp;
16025         this.maxY = this.initPageY + iDown;
16026         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
16027
16028         this.constrainY = true;
16029
16030     },
16031
16032     /**
16033      * resetConstraints must be called if you manually reposition a dd element.
16034      * @method resetConstraints
16035      * @param {boolean} maintainOffset
16036      */
16037     resetConstraints: function() {
16038
16039
16040         // Maintain offsets if necessary
16041         if (this.initPageX || this.initPageX === 0) {
16042             // figure out how much this thing has moved
16043             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
16044             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
16045
16046             this.setInitPosition(dx, dy);
16047
16048         // This is the first time we have detected the element's position
16049         } else {
16050             this.setInitPosition();
16051         }
16052
16053         if (this.constrainX) {
16054             this.setXConstraint( this.leftConstraint,
16055                                  this.rightConstraint,
16056                                  this.xTickSize        );
16057         }
16058
16059         if (this.constrainY) {
16060             this.setYConstraint( this.topConstraint,
16061                                  this.bottomConstraint,
16062                                  this.yTickSize         );
16063         }
16064     },
16065
16066     /**
16067      * Normally the drag element is moved pixel by pixel, but we can specify
16068      * that it move a number of pixels at a time.  This method resolves the
16069      * location when we have it set up like this.
16070      * @method getTick
16071      * @param {int} val where we want to place the object
16072      * @param {int[]} tickArray sorted array of valid points
16073      * @return {int} the closest tick
16074      * @private
16075      */
16076     getTick: function(val, tickArray) {
16077
16078         if (!tickArray) {
16079             // If tick interval is not defined, it is effectively 1 pixel,
16080             // so we return the value passed to us.
16081             return val;
16082         } else if (tickArray[0] >= val) {
16083             // The value is lower than the first tick, so we return the first
16084             // tick.
16085             return tickArray[0];
16086         } else {
16087             for (var i=0, len=tickArray.length; i<len; ++i) {
16088                 var next = i + 1;
16089                 if (tickArray[next] && tickArray[next] >= val) {
16090                     var diff1 = val - tickArray[i];
16091                     var diff2 = tickArray[next] - val;
16092                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
16093                 }
16094             }
16095
16096             // The value is larger than the last tick, so we return the last
16097             // tick.
16098             return tickArray[tickArray.length - 1];
16099         }
16100     },
16101
16102     /**
16103      * toString method
16104      * @method toString
16105      * @return {string} string representation of the dd obj
16106      */
16107     toString: function() {
16108         return ("DragDrop " + this.id);
16109     }
16110
16111 });
16112
16113 })();
16114 /*
16115  * Based on:
16116  * Ext JS Library 1.1.1
16117  * Copyright(c) 2006-2007, Ext JS, LLC.
16118  *
16119  * Originally Released Under LGPL - original licence link has changed is not relivant.
16120  *
16121  * Fork - LGPL
16122  * <script type="text/javascript">
16123  */
16124
16125
16126 /**
16127  * The drag and drop utility provides a framework for building drag and drop
16128  * applications.  In addition to enabling drag and drop for specific elements,
16129  * the drag and drop elements are tracked by the manager class, and the
16130  * interactions between the various elements are tracked during the drag and
16131  * the implementing code is notified about these important moments.
16132  */
16133
16134 // Only load the library once.  Rewriting the manager class would orphan
16135 // existing drag and drop instances.
16136 if (!Roo.dd.DragDropMgr) {
16137
16138 /**
16139  * @class Roo.dd.DragDropMgr
16140  * DragDropMgr is a singleton that tracks the element interaction for
16141  * all DragDrop items in the window.  Generally, you will not call
16142  * this class directly, but it does have helper methods that could
16143  * be useful in your DragDrop implementations.
16144  * @singleton
16145  */
16146 Roo.dd.DragDropMgr = function() {
16147
16148     var Event = Roo.EventManager;
16149
16150     return {
16151
16152         /**
16153          * Two dimensional Array of registered DragDrop objects.  The first
16154          * dimension is the DragDrop item group, the second the DragDrop
16155          * object.
16156          * @property ids
16157          * @type {string: string}
16158          * @private
16159          * @static
16160          */
16161         ids: {},
16162
16163         /**
16164          * Array of element ids defined as drag handles.  Used to determine
16165          * if the element that generated the mousedown event is actually the
16166          * handle and not the html element itself.
16167          * @property handleIds
16168          * @type {string: string}
16169          * @private
16170          * @static
16171          */
16172         handleIds: {},
16173
16174         /**
16175          * the DragDrop object that is currently being dragged
16176          * @property dragCurrent
16177          * @type DragDrop
16178          * @private
16179          * @static
16180          **/
16181         dragCurrent: null,
16182
16183         /**
16184          * the DragDrop object(s) that are being hovered over
16185          * @property dragOvers
16186          * @type Array
16187          * @private
16188          * @static
16189          */
16190         dragOvers: {},
16191
16192         /**
16193          * the X distance between the cursor and the object being dragged
16194          * @property deltaX
16195          * @type int
16196          * @private
16197          * @static
16198          */
16199         deltaX: 0,
16200
16201         /**
16202          * the Y distance between the cursor and the object being dragged
16203          * @property deltaY
16204          * @type int
16205          * @private
16206          * @static
16207          */
16208         deltaY: 0,
16209
16210         /**
16211          * Flag to determine if we should prevent the default behavior of the
16212          * events we define. By default this is true, but this can be set to
16213          * false if you need the default behavior (not recommended)
16214          * @property preventDefault
16215          * @type boolean
16216          * @static
16217          */
16218         preventDefault: true,
16219
16220         /**
16221          * Flag to determine if we should stop the propagation of the events
16222          * we generate. This is true by default but you may want to set it to
16223          * false if the html element contains other features that require the
16224          * mouse click.
16225          * @property stopPropagation
16226          * @type boolean
16227          * @static
16228          */
16229         stopPropagation: true,
16230
16231         /**
16232          * Internal flag that is set to true when drag and drop has been
16233          * intialized
16234          * @property initialized
16235          * @private
16236          * @static
16237          */
16238         initalized: false,
16239
16240         /**
16241          * All drag and drop can be disabled.
16242          * @property locked
16243          * @private
16244          * @static
16245          */
16246         locked: false,
16247
16248         /**
16249          * Called the first time an element is registered.
16250          * @method init
16251          * @private
16252          * @static
16253          */
16254         init: function() {
16255             this.initialized = true;
16256         },
16257
16258         /**
16259          * In point mode, drag and drop interaction is defined by the
16260          * location of the cursor during the drag/drop
16261          * @property POINT
16262          * @type int
16263          * @static
16264          */
16265         POINT: 0,
16266
16267         /**
16268          * In intersect mode, drag and drop interactio nis defined by the
16269          * overlap of two or more drag and drop objects.
16270          * @property INTERSECT
16271          * @type int
16272          * @static
16273          */
16274         INTERSECT: 1,
16275
16276         /**
16277          * The current drag and drop mode.  Default: POINT
16278          * @property mode
16279          * @type int
16280          * @static
16281          */
16282         mode: 0,
16283
16284         /**
16285          * Runs method on all drag and drop objects
16286          * @method _execOnAll
16287          * @private
16288          * @static
16289          */
16290         _execOnAll: function(sMethod, args) {
16291             for (var i in this.ids) {
16292                 for (var j in this.ids[i]) {
16293                     var oDD = this.ids[i][j];
16294                     if (! this.isTypeOfDD(oDD)) {
16295                         continue;
16296                     }
16297                     oDD[sMethod].apply(oDD, args);
16298                 }
16299             }
16300         },
16301
16302         /**
16303          * Drag and drop initialization.  Sets up the global event handlers
16304          * @method _onLoad
16305          * @private
16306          * @static
16307          */
16308         _onLoad: function() {
16309
16310             this.init();
16311
16312
16313             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
16314             Event.on(document, "mousemove", this.handleMouseMove, this, true);
16315             Event.on(window,   "unload",    this._onUnload, this, true);
16316             Event.on(window,   "resize",    this._onResize, this, true);
16317             // Event.on(window,   "mouseout",    this._test);
16318
16319         },
16320
16321         /**
16322          * Reset constraints on all drag and drop objs
16323          * @method _onResize
16324          * @private
16325          * @static
16326          */
16327         _onResize: function(e) {
16328             this._execOnAll("resetConstraints", []);
16329         },
16330
16331         /**
16332          * Lock all drag and drop functionality
16333          * @method lock
16334          * @static
16335          */
16336         lock: function() { this.locked = true; },
16337
16338         /**
16339          * Unlock all drag and drop functionality
16340          * @method unlock
16341          * @static
16342          */
16343         unlock: function() { this.locked = false; },
16344
16345         /**
16346          * Is drag and drop locked?
16347          * @method isLocked
16348          * @return {boolean} True if drag and drop is locked, false otherwise.
16349          * @static
16350          */
16351         isLocked: function() { return this.locked; },
16352
16353         /**
16354          * Location cache that is set for all drag drop objects when a drag is
16355          * initiated, cleared when the drag is finished.
16356          * @property locationCache
16357          * @private
16358          * @static
16359          */
16360         locationCache: {},
16361
16362         /**
16363          * Set useCache to false if you want to force object the lookup of each
16364          * drag and drop linked element constantly during a drag.
16365          * @property useCache
16366          * @type boolean
16367          * @static
16368          */
16369         useCache: true,
16370
16371         /**
16372          * The number of pixels that the mouse needs to move after the
16373          * mousedown before the drag is initiated.  Default=3;
16374          * @property clickPixelThresh
16375          * @type int
16376          * @static
16377          */
16378         clickPixelThresh: 3,
16379
16380         /**
16381          * The number of milliseconds after the mousedown event to initiate the
16382          * drag if we don't get a mouseup event. Default=1000
16383          * @property clickTimeThresh
16384          * @type int
16385          * @static
16386          */
16387         clickTimeThresh: 350,
16388
16389         /**
16390          * Flag that indicates that either the drag pixel threshold or the
16391          * mousdown time threshold has been met
16392          * @property dragThreshMet
16393          * @type boolean
16394          * @private
16395          * @static
16396          */
16397         dragThreshMet: false,
16398
16399         /**
16400          * Timeout used for the click time threshold
16401          * @property clickTimeout
16402          * @type Object
16403          * @private
16404          * @static
16405          */
16406         clickTimeout: null,
16407
16408         /**
16409          * The X position of the mousedown event stored for later use when a
16410          * drag threshold is met.
16411          * @property startX
16412          * @type int
16413          * @private
16414          * @static
16415          */
16416         startX: 0,
16417
16418         /**
16419          * The Y position of the mousedown event stored for later use when a
16420          * drag threshold is met.
16421          * @property startY
16422          * @type int
16423          * @private
16424          * @static
16425          */
16426         startY: 0,
16427
16428         /**
16429          * Each DragDrop instance must be registered with the DragDropMgr.
16430          * This is executed in DragDrop.init()
16431          * @method regDragDrop
16432          * @param {DragDrop} oDD the DragDrop object to register
16433          * @param {String} sGroup the name of the group this element belongs to
16434          * @static
16435          */
16436         regDragDrop: function(oDD, sGroup) {
16437             if (!this.initialized) { this.init(); }
16438
16439             if (!this.ids[sGroup]) {
16440                 this.ids[sGroup] = {};
16441             }
16442             this.ids[sGroup][oDD.id] = oDD;
16443         },
16444
16445         /**
16446          * Removes the supplied dd instance from the supplied group. Executed
16447          * by DragDrop.removeFromGroup, so don't call this function directly.
16448          * @method removeDDFromGroup
16449          * @private
16450          * @static
16451          */
16452         removeDDFromGroup: function(oDD, sGroup) {
16453             if (!this.ids[sGroup]) {
16454                 this.ids[sGroup] = {};
16455             }
16456
16457             var obj = this.ids[sGroup];
16458             if (obj && obj[oDD.id]) {
16459                 delete obj[oDD.id];
16460             }
16461         },
16462
16463         /**
16464          * Unregisters a drag and drop item.  This is executed in
16465          * DragDrop.unreg, use that method instead of calling this directly.
16466          * @method _remove
16467          * @private
16468          * @static
16469          */
16470         _remove: function(oDD) {
16471             for (var g in oDD.groups) {
16472                 if (g && this.ids[g][oDD.id]) {
16473                     delete this.ids[g][oDD.id];
16474                 }
16475             }
16476             delete this.handleIds[oDD.id];
16477         },
16478
16479         /**
16480          * Each DragDrop handle element must be registered.  This is done
16481          * automatically when executing DragDrop.setHandleElId()
16482          * @method regHandle
16483          * @param {String} sDDId the DragDrop id this element is a handle for
16484          * @param {String} sHandleId the id of the element that is the drag
16485          * handle
16486          * @static
16487          */
16488         regHandle: function(sDDId, sHandleId) {
16489             if (!this.handleIds[sDDId]) {
16490                 this.handleIds[sDDId] = {};
16491             }
16492             this.handleIds[sDDId][sHandleId] = sHandleId;
16493         },
16494
16495         /**
16496          * Utility function to determine if a given element has been
16497          * registered as a drag drop item.
16498          * @method isDragDrop
16499          * @param {String} id the element id to check
16500          * @return {boolean} true if this element is a DragDrop item,
16501          * false otherwise
16502          * @static
16503          */
16504         isDragDrop: function(id) {
16505             return ( this.getDDById(id) ) ? true : false;
16506         },
16507
16508         /**
16509          * Returns the drag and drop instances that are in all groups the
16510          * passed in instance belongs to.
16511          * @method getRelated
16512          * @param {DragDrop} p_oDD the obj to get related data for
16513          * @param {boolean} bTargetsOnly if true, only return targetable objs
16514          * @return {DragDrop[]} the related instances
16515          * @static
16516          */
16517         getRelated: function(p_oDD, bTargetsOnly) {
16518             var oDDs = [];
16519             for (var i in p_oDD.groups) {
16520                 for (j in this.ids[i]) {
16521                     var dd = this.ids[i][j];
16522                     if (! this.isTypeOfDD(dd)) {
16523                         continue;
16524                     }
16525                     if (!bTargetsOnly || dd.isTarget) {
16526                         oDDs[oDDs.length] = dd;
16527                     }
16528                 }
16529             }
16530
16531             return oDDs;
16532         },
16533
16534         /**
16535          * Returns true if the specified dd target is a legal target for
16536          * the specifice drag obj
16537          * @method isLegalTarget
16538          * @param {DragDrop} the drag obj
16539          * @param {DragDrop} the target
16540          * @return {boolean} true if the target is a legal target for the
16541          * dd obj
16542          * @static
16543          */
16544         isLegalTarget: function (oDD, oTargetDD) {
16545             var targets = this.getRelated(oDD, true);
16546             for (var i=0, len=targets.length;i<len;++i) {
16547                 if (targets[i].id == oTargetDD.id) {
16548                     return true;
16549                 }
16550             }
16551
16552             return false;
16553         },
16554
16555         /**
16556          * My goal is to be able to transparently determine if an object is
16557          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16558          * returns "object", oDD.constructor.toString() always returns
16559          * "DragDrop" and not the name of the subclass.  So for now it just
16560          * evaluates a well-known variable in DragDrop.
16561          * @method isTypeOfDD
16562          * @param {Object} the object to evaluate
16563          * @return {boolean} true if typeof oDD = DragDrop
16564          * @static
16565          */
16566         isTypeOfDD: function (oDD) {
16567             return (oDD && oDD.__ygDragDrop);
16568         },
16569
16570         /**
16571          * Utility function to determine if a given element has been
16572          * registered as a drag drop handle for the given Drag Drop object.
16573          * @method isHandle
16574          * @param {String} id the element id to check
16575          * @return {boolean} true if this element is a DragDrop handle, false
16576          * otherwise
16577          * @static
16578          */
16579         isHandle: function(sDDId, sHandleId) {
16580             return ( this.handleIds[sDDId] &&
16581                             this.handleIds[sDDId][sHandleId] );
16582         },
16583
16584         /**
16585          * Returns the DragDrop instance for a given id
16586          * @method getDDById
16587          * @param {String} id the id of the DragDrop object
16588          * @return {DragDrop} the drag drop object, null if it is not found
16589          * @static
16590          */
16591         getDDById: function(id) {
16592             for (var i in this.ids) {
16593                 if (this.ids[i][id]) {
16594                     return this.ids[i][id];
16595                 }
16596             }
16597             return null;
16598         },
16599
16600         /**
16601          * Fired after a registered DragDrop object gets the mousedown event.
16602          * Sets up the events required to track the object being dragged
16603          * @method handleMouseDown
16604          * @param {Event} e the event
16605          * @param oDD the DragDrop object being dragged
16606          * @private
16607          * @static
16608          */
16609         handleMouseDown: function(e, oDD) {
16610             if(Roo.QuickTips){
16611                 Roo.QuickTips.disable();
16612             }
16613             this.currentTarget = e.getTarget();
16614
16615             this.dragCurrent = oDD;
16616
16617             var el = oDD.getEl();
16618
16619             // track start position
16620             this.startX = e.getPageX();
16621             this.startY = e.getPageY();
16622
16623             this.deltaX = this.startX - el.offsetLeft;
16624             this.deltaY = this.startY - el.offsetTop;
16625
16626             this.dragThreshMet = false;
16627
16628             this.clickTimeout = setTimeout(
16629                     function() {
16630                         var DDM = Roo.dd.DDM;
16631                         DDM.startDrag(DDM.startX, DDM.startY);
16632                     },
16633                     this.clickTimeThresh );
16634         },
16635
16636         /**
16637          * Fired when either the drag pixel threshol or the mousedown hold
16638          * time threshold has been met.
16639          * @method startDrag
16640          * @param x {int} the X position of the original mousedown
16641          * @param y {int} the Y position of the original mousedown
16642          * @static
16643          */
16644         startDrag: function(x, y) {
16645             clearTimeout(this.clickTimeout);
16646             if (this.dragCurrent) {
16647                 this.dragCurrent.b4StartDrag(x, y);
16648                 this.dragCurrent.startDrag(x, y);
16649             }
16650             this.dragThreshMet = true;
16651         },
16652
16653         /**
16654          * Internal function to handle the mouseup event.  Will be invoked
16655          * from the context of the document.
16656          * @method handleMouseUp
16657          * @param {Event} e the event
16658          * @private
16659          * @static
16660          */
16661         handleMouseUp: function(e) {
16662
16663             if(Roo.QuickTips){
16664                 Roo.QuickTips.enable();
16665             }
16666             if (! this.dragCurrent) {
16667                 return;
16668             }
16669
16670             clearTimeout(this.clickTimeout);
16671
16672             if (this.dragThreshMet) {
16673                 this.fireEvents(e, true);
16674             } else {
16675             }
16676
16677             this.stopDrag(e);
16678
16679             this.stopEvent(e);
16680         },
16681
16682         /**
16683          * Utility to stop event propagation and event default, if these
16684          * features are turned on.
16685          * @method stopEvent
16686          * @param {Event} e the event as returned by this.getEvent()
16687          * @static
16688          */
16689         stopEvent: function(e){
16690             if(this.stopPropagation) {
16691                 e.stopPropagation();
16692             }
16693
16694             if (this.preventDefault) {
16695                 e.preventDefault();
16696             }
16697         },
16698
16699         /**
16700          * Internal function to clean up event handlers after the drag
16701          * operation is complete
16702          * @method stopDrag
16703          * @param {Event} e the event
16704          * @private
16705          * @static
16706          */
16707         stopDrag: function(e) {
16708             // Fire the drag end event for the item that was dragged
16709             if (this.dragCurrent) {
16710                 if (this.dragThreshMet) {
16711                     this.dragCurrent.b4EndDrag(e);
16712                     this.dragCurrent.endDrag(e);
16713                 }
16714
16715                 this.dragCurrent.onMouseUp(e);
16716             }
16717
16718             this.dragCurrent = null;
16719             this.dragOvers = {};
16720         },
16721
16722         /**
16723          * Internal function to handle the mousemove event.  Will be invoked
16724          * from the context of the html element.
16725          *
16726          * @TODO figure out what we can do about mouse events lost when the
16727          * user drags objects beyond the window boundary.  Currently we can
16728          * detect this in internet explorer by verifying that the mouse is
16729          * down during the mousemove event.  Firefox doesn't give us the
16730          * button state on the mousemove event.
16731          * @method handleMouseMove
16732          * @param {Event} e the event
16733          * @private
16734          * @static
16735          */
16736         handleMouseMove: function(e) {
16737             if (! this.dragCurrent) {
16738                 return true;
16739             }
16740
16741             // var button = e.which || e.button;
16742
16743             // check for IE mouseup outside of page boundary
16744             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16745                 this.stopEvent(e);
16746                 return this.handleMouseUp(e);
16747             }
16748
16749             if (!this.dragThreshMet) {
16750                 var diffX = Math.abs(this.startX - e.getPageX());
16751                 var diffY = Math.abs(this.startY - e.getPageY());
16752                 if (diffX > this.clickPixelThresh ||
16753                             diffY > this.clickPixelThresh) {
16754                     this.startDrag(this.startX, this.startY);
16755                 }
16756             }
16757
16758             if (this.dragThreshMet) {
16759                 this.dragCurrent.b4Drag(e);
16760                 this.dragCurrent.onDrag(e);
16761                 if(!this.dragCurrent.moveOnly){
16762                     this.fireEvents(e, false);
16763                 }
16764             }
16765
16766             this.stopEvent(e);
16767
16768             return true;
16769         },
16770
16771         /**
16772          * Iterates over all of the DragDrop elements to find ones we are
16773          * hovering over or dropping on
16774          * @method fireEvents
16775          * @param {Event} e the event
16776          * @param {boolean} isDrop is this a drop op or a mouseover op?
16777          * @private
16778          * @static
16779          */
16780         fireEvents: function(e, isDrop) {
16781             var dc = this.dragCurrent;
16782
16783             // If the user did the mouse up outside of the window, we could
16784             // get here even though we have ended the drag.
16785             if (!dc || dc.isLocked()) {
16786                 return;
16787             }
16788
16789             var pt = e.getPoint();
16790
16791             // cache the previous dragOver array
16792             var oldOvers = [];
16793
16794             var outEvts   = [];
16795             var overEvts  = [];
16796             var dropEvts  = [];
16797             var enterEvts = [];
16798
16799             // Check to see if the object(s) we were hovering over is no longer
16800             // being hovered over so we can fire the onDragOut event
16801             for (var i in this.dragOvers) {
16802
16803                 var ddo = this.dragOvers[i];
16804
16805                 if (! this.isTypeOfDD(ddo)) {
16806                     continue;
16807                 }
16808
16809                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16810                     outEvts.push( ddo );
16811                 }
16812
16813                 oldOvers[i] = true;
16814                 delete this.dragOvers[i];
16815             }
16816
16817             for (var sGroup in dc.groups) {
16818
16819                 if ("string" != typeof sGroup) {
16820                     continue;
16821                 }
16822
16823                 for (i in this.ids[sGroup]) {
16824                     var oDD = this.ids[sGroup][i];
16825                     if (! this.isTypeOfDD(oDD)) {
16826                         continue;
16827                     }
16828
16829                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16830                         if (this.isOverTarget(pt, oDD, this.mode)) {
16831                             // look for drop interactions
16832                             if (isDrop) {
16833                                 dropEvts.push( oDD );
16834                             // look for drag enter and drag over interactions
16835                             } else {
16836
16837                                 // initial drag over: dragEnter fires
16838                                 if (!oldOvers[oDD.id]) {
16839                                     enterEvts.push( oDD );
16840                                 // subsequent drag overs: dragOver fires
16841                                 } else {
16842                                     overEvts.push( oDD );
16843                                 }
16844
16845                                 this.dragOvers[oDD.id] = oDD;
16846                             }
16847                         }
16848                     }
16849                 }
16850             }
16851
16852             if (this.mode) {
16853                 if (outEvts.length) {
16854                     dc.b4DragOut(e, outEvts);
16855                     dc.onDragOut(e, outEvts);
16856                 }
16857
16858                 if (enterEvts.length) {
16859                     dc.onDragEnter(e, enterEvts);
16860                 }
16861
16862                 if (overEvts.length) {
16863                     dc.b4DragOver(e, overEvts);
16864                     dc.onDragOver(e, overEvts);
16865                 }
16866
16867                 if (dropEvts.length) {
16868                     dc.b4DragDrop(e, dropEvts);
16869                     dc.onDragDrop(e, dropEvts);
16870                 }
16871
16872             } else {
16873                 // fire dragout events
16874                 var len = 0;
16875                 for (i=0, len=outEvts.length; i<len; ++i) {
16876                     dc.b4DragOut(e, outEvts[i].id);
16877                     dc.onDragOut(e, outEvts[i].id);
16878                 }
16879
16880                 // fire enter events
16881                 for (i=0,len=enterEvts.length; i<len; ++i) {
16882                     // dc.b4DragEnter(e, oDD.id);
16883                     dc.onDragEnter(e, enterEvts[i].id);
16884                 }
16885
16886                 // fire over events
16887                 for (i=0,len=overEvts.length; i<len; ++i) {
16888                     dc.b4DragOver(e, overEvts[i].id);
16889                     dc.onDragOver(e, overEvts[i].id);
16890                 }
16891
16892                 // fire drop events
16893                 for (i=0, len=dropEvts.length; i<len; ++i) {
16894                     dc.b4DragDrop(e, dropEvts[i].id);
16895                     dc.onDragDrop(e, dropEvts[i].id);
16896                 }
16897
16898             }
16899
16900             // notify about a drop that did not find a target
16901             if (isDrop && !dropEvts.length) {
16902                 dc.onInvalidDrop(e);
16903             }
16904
16905         },
16906
16907         /**
16908          * Helper function for getting the best match from the list of drag
16909          * and drop objects returned by the drag and drop events when we are
16910          * in INTERSECT mode.  It returns either the first object that the
16911          * cursor is over, or the object that has the greatest overlap with
16912          * the dragged element.
16913          * @method getBestMatch
16914          * @param  {DragDrop[]} dds The array of drag and drop objects
16915          * targeted
16916          * @return {DragDrop}       The best single match
16917          * @static
16918          */
16919         getBestMatch: function(dds) {
16920             var winner = null;
16921             // Return null if the input is not what we expect
16922             //if (!dds || !dds.length || dds.length == 0) {
16923                // winner = null;
16924             // If there is only one item, it wins
16925             //} else if (dds.length == 1) {
16926
16927             var len = dds.length;
16928
16929             if (len == 1) {
16930                 winner = dds[0];
16931             } else {
16932                 // Loop through the targeted items
16933                 for (var i=0; i<len; ++i) {
16934                     var dd = dds[i];
16935                     // If the cursor is over the object, it wins.  If the
16936                     // cursor is over multiple matches, the first one we come
16937                     // to wins.
16938                     if (dd.cursorIsOver) {
16939                         winner = dd;
16940                         break;
16941                     // Otherwise the object with the most overlap wins
16942                     } else {
16943                         if (!winner ||
16944                             winner.overlap.getArea() < dd.overlap.getArea()) {
16945                             winner = dd;
16946                         }
16947                     }
16948                 }
16949             }
16950
16951             return winner;
16952         },
16953
16954         /**
16955          * Refreshes the cache of the top-left and bottom-right points of the
16956          * drag and drop objects in the specified group(s).  This is in the
16957          * format that is stored in the drag and drop instance, so typical
16958          * usage is:
16959          * <code>
16960          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16961          * </code>
16962          * Alternatively:
16963          * <code>
16964          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16965          * </code>
16966          * @TODO this really should be an indexed array.  Alternatively this
16967          * method could accept both.
16968          * @method refreshCache
16969          * @param {Object} groups an associative array of groups to refresh
16970          * @static
16971          */
16972         refreshCache: function(groups) {
16973             for (var sGroup in groups) {
16974                 if ("string" != typeof sGroup) {
16975                     continue;
16976                 }
16977                 for (var i in this.ids[sGroup]) {
16978                     var oDD = this.ids[sGroup][i];
16979
16980                     if (this.isTypeOfDD(oDD)) {
16981                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16982                         var loc = this.getLocation(oDD);
16983                         if (loc) {
16984                             this.locationCache[oDD.id] = loc;
16985                         } else {
16986                             delete this.locationCache[oDD.id];
16987                             // this will unregister the drag and drop object if
16988                             // the element is not in a usable state
16989                             // oDD.unreg();
16990                         }
16991                     }
16992                 }
16993             }
16994         },
16995
16996         /**
16997          * This checks to make sure an element exists and is in the DOM.  The
16998          * main purpose is to handle cases where innerHTML is used to remove
16999          * drag and drop objects from the DOM.  IE provides an 'unspecified
17000          * error' when trying to access the offsetParent of such an element
17001          * @method verifyEl
17002          * @param {HTMLElement} el the element to check
17003          * @return {boolean} true if the element looks usable
17004          * @static
17005          */
17006         verifyEl: function(el) {
17007             if (el) {
17008                 var parent;
17009                 if(Roo.isIE){
17010                     try{
17011                         parent = el.offsetParent;
17012                     }catch(e){}
17013                 }else{
17014                     parent = el.offsetParent;
17015                 }
17016                 if (parent) {
17017                     return true;
17018                 }
17019             }
17020
17021             return false;
17022         },
17023
17024         /**
17025          * Returns a Region object containing the drag and drop element's position
17026          * and size, including the padding configured for it
17027          * @method getLocation
17028          * @param {DragDrop} oDD the drag and drop object to get the
17029          *                       location for
17030          * @return {Roo.lib.Region} a Region object representing the total area
17031          *                             the element occupies, including any padding
17032          *                             the instance is configured for.
17033          * @static
17034          */
17035         getLocation: function(oDD) {
17036             if (! this.isTypeOfDD(oDD)) {
17037                 return null;
17038             }
17039
17040             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
17041
17042             try {
17043                 pos= Roo.lib.Dom.getXY(el);
17044             } catch (e) { }
17045
17046             if (!pos) {
17047                 return null;
17048             }
17049
17050             x1 = pos[0];
17051             x2 = x1 + el.offsetWidth;
17052             y1 = pos[1];
17053             y2 = y1 + el.offsetHeight;
17054
17055             t = y1 - oDD.padding[0];
17056             r = x2 + oDD.padding[1];
17057             b = y2 + oDD.padding[2];
17058             l = x1 - oDD.padding[3];
17059
17060             return new Roo.lib.Region( t, r, b, l );
17061         },
17062
17063         /**
17064          * Checks the cursor location to see if it over the target
17065          * @method isOverTarget
17066          * @param {Roo.lib.Point} pt The point to evaluate
17067          * @param {DragDrop} oTarget the DragDrop object we are inspecting
17068          * @return {boolean} true if the mouse is over the target
17069          * @private
17070          * @static
17071          */
17072         isOverTarget: function(pt, oTarget, intersect) {
17073             // use cache if available
17074             var loc = this.locationCache[oTarget.id];
17075             if (!loc || !this.useCache) {
17076                 loc = this.getLocation(oTarget);
17077                 this.locationCache[oTarget.id] = loc;
17078
17079             }
17080
17081             if (!loc) {
17082                 return false;
17083             }
17084
17085             oTarget.cursorIsOver = loc.contains( pt );
17086
17087             // DragDrop is using this as a sanity check for the initial mousedown
17088             // in this case we are done.  In POINT mode, if the drag obj has no
17089             // contraints, we are also done. Otherwise we need to evaluate the
17090             // location of the target as related to the actual location of the
17091             // dragged element.
17092             var dc = this.dragCurrent;
17093             if (!dc || !dc.getTargetCoord ||
17094                     (!intersect && !dc.constrainX && !dc.constrainY)) {
17095                 return oTarget.cursorIsOver;
17096             }
17097
17098             oTarget.overlap = null;
17099
17100             // Get the current location of the drag element, this is the
17101             // location of the mouse event less the delta that represents
17102             // where the original mousedown happened on the element.  We
17103             // need to consider constraints and ticks as well.
17104             var pos = dc.getTargetCoord(pt.x, pt.y);
17105
17106             var el = dc.getDragEl();
17107             var curRegion = new Roo.lib.Region( pos.y,
17108                                                    pos.x + el.offsetWidth,
17109                                                    pos.y + el.offsetHeight,
17110                                                    pos.x );
17111
17112             var overlap = curRegion.intersect(loc);
17113
17114             if (overlap) {
17115                 oTarget.overlap = overlap;
17116                 return (intersect) ? true : oTarget.cursorIsOver;
17117             } else {
17118                 return false;
17119             }
17120         },
17121
17122         /**
17123          * unload event handler
17124          * @method _onUnload
17125          * @private
17126          * @static
17127          */
17128         _onUnload: function(e, me) {
17129             Roo.dd.DragDropMgr.unregAll();
17130         },
17131
17132         /**
17133          * Cleans up the drag and drop events and objects.
17134          * @method unregAll
17135          * @private
17136          * @static
17137          */
17138         unregAll: function() {
17139
17140             if (this.dragCurrent) {
17141                 this.stopDrag();
17142                 this.dragCurrent = null;
17143             }
17144
17145             this._execOnAll("unreg", []);
17146
17147             for (i in this.elementCache) {
17148                 delete this.elementCache[i];
17149             }
17150
17151             this.elementCache = {};
17152             this.ids = {};
17153         },
17154
17155         /**
17156          * A cache of DOM elements
17157          * @property elementCache
17158          * @private
17159          * @static
17160          */
17161         elementCache: {},
17162
17163         /**
17164          * Get the wrapper for the DOM element specified
17165          * @method getElWrapper
17166          * @param {String} id the id of the element to get
17167          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
17168          * @private
17169          * @deprecated This wrapper isn't that useful
17170          * @static
17171          */
17172         getElWrapper: function(id) {
17173             var oWrapper = this.elementCache[id];
17174             if (!oWrapper || !oWrapper.el) {
17175                 oWrapper = this.elementCache[id] =
17176                     new this.ElementWrapper(Roo.getDom(id));
17177             }
17178             return oWrapper;
17179         },
17180
17181         /**
17182          * Returns the actual DOM element
17183          * @method getElement
17184          * @param {String} id the id of the elment to get
17185          * @return {Object} The element
17186          * @deprecated use Roo.getDom instead
17187          * @static
17188          */
17189         getElement: function(id) {
17190             return Roo.getDom(id);
17191         },
17192
17193         /**
17194          * Returns the style property for the DOM element (i.e.,
17195          * document.getElById(id).style)
17196          * @method getCss
17197          * @param {String} id the id of the elment to get
17198          * @return {Object} The style property of the element
17199          * @deprecated use Roo.getDom instead
17200          * @static
17201          */
17202         getCss: function(id) {
17203             var el = Roo.getDom(id);
17204             return (el) ? el.style : null;
17205         },
17206
17207         /**
17208          * Inner class for cached elements
17209          * @class DragDropMgr.ElementWrapper
17210          * @for DragDropMgr
17211          * @private
17212          * @deprecated
17213          */
17214         ElementWrapper: function(el) {
17215                 /**
17216                  * The element
17217                  * @property el
17218                  */
17219                 this.el = el || null;
17220                 /**
17221                  * The element id
17222                  * @property id
17223                  */
17224                 this.id = this.el && el.id;
17225                 /**
17226                  * A reference to the style property
17227                  * @property css
17228                  */
17229                 this.css = this.el && el.style;
17230             },
17231
17232         /**
17233          * Returns the X position of an html element
17234          * @method getPosX
17235          * @param el the element for which to get the position
17236          * @return {int} the X coordinate
17237          * @for DragDropMgr
17238          * @deprecated use Roo.lib.Dom.getX instead
17239          * @static
17240          */
17241         getPosX: function(el) {
17242             return Roo.lib.Dom.getX(el);
17243         },
17244
17245         /**
17246          * Returns the Y position of an html element
17247          * @method getPosY
17248          * @param el the element for which to get the position
17249          * @return {int} the Y coordinate
17250          * @deprecated use Roo.lib.Dom.getY instead
17251          * @static
17252          */
17253         getPosY: function(el) {
17254             return Roo.lib.Dom.getY(el);
17255         },
17256
17257         /**
17258          * Swap two nodes.  In IE, we use the native method, for others we
17259          * emulate the IE behavior
17260          * @method swapNode
17261          * @param n1 the first node to swap
17262          * @param n2 the other node to swap
17263          * @static
17264          */
17265         swapNode: function(n1, n2) {
17266             if (n1.swapNode) {
17267                 n1.swapNode(n2);
17268             } else {
17269                 var p = n2.parentNode;
17270                 var s = n2.nextSibling;
17271
17272                 if (s == n1) {
17273                     p.insertBefore(n1, n2);
17274                 } else if (n2 == n1.nextSibling) {
17275                     p.insertBefore(n2, n1);
17276                 } else {
17277                     n1.parentNode.replaceChild(n2, n1);
17278                     p.insertBefore(n1, s);
17279                 }
17280             }
17281         },
17282
17283         /**
17284          * Returns the current scroll position
17285          * @method getScroll
17286          * @private
17287          * @static
17288          */
17289         getScroll: function () {
17290             var t, l, dde=document.documentElement, db=document.body;
17291             if (dde && (dde.scrollTop || dde.scrollLeft)) {
17292                 t = dde.scrollTop;
17293                 l = dde.scrollLeft;
17294             } else if (db) {
17295                 t = db.scrollTop;
17296                 l = db.scrollLeft;
17297             } else {
17298
17299             }
17300             return { top: t, left: l };
17301         },
17302
17303         /**
17304          * Returns the specified element style property
17305          * @method getStyle
17306          * @param {HTMLElement} el          the element
17307          * @param {string}      styleProp   the style property
17308          * @return {string} The value of the style property
17309          * @deprecated use Roo.lib.Dom.getStyle
17310          * @static
17311          */
17312         getStyle: function(el, styleProp) {
17313             return Roo.fly(el).getStyle(styleProp);
17314         },
17315
17316         /**
17317          * Gets the scrollTop
17318          * @method getScrollTop
17319          * @return {int} the document's scrollTop
17320          * @static
17321          */
17322         getScrollTop: function () { return this.getScroll().top; },
17323
17324         /**
17325          * Gets the scrollLeft
17326          * @method getScrollLeft
17327          * @return {int} the document's scrollTop
17328          * @static
17329          */
17330         getScrollLeft: function () { return this.getScroll().left; },
17331
17332         /**
17333          * Sets the x/y position of an element to the location of the
17334          * target element.
17335          * @method moveToEl
17336          * @param {HTMLElement} moveEl      The element to move
17337          * @param {HTMLElement} targetEl    The position reference element
17338          * @static
17339          */
17340         moveToEl: function (moveEl, targetEl) {
17341             var aCoord = Roo.lib.Dom.getXY(targetEl);
17342             Roo.lib.Dom.setXY(moveEl, aCoord);
17343         },
17344
17345         /**
17346          * Numeric array sort function
17347          * @method numericSort
17348          * @static
17349          */
17350         numericSort: function(a, b) { return (a - b); },
17351
17352         /**
17353          * Internal counter
17354          * @property _timeoutCount
17355          * @private
17356          * @static
17357          */
17358         _timeoutCount: 0,
17359
17360         /**
17361          * Trying to make the load order less important.  Without this we get
17362          * an error if this file is loaded before the Event Utility.
17363          * @method _addListeners
17364          * @private
17365          * @static
17366          */
17367         _addListeners: function() {
17368             var DDM = Roo.dd.DDM;
17369             if ( Roo.lib.Event && document ) {
17370                 DDM._onLoad();
17371             } else {
17372                 if (DDM._timeoutCount > 2000) {
17373                 } else {
17374                     setTimeout(DDM._addListeners, 10);
17375                     if (document && document.body) {
17376                         DDM._timeoutCount += 1;
17377                     }
17378                 }
17379             }
17380         },
17381
17382         /**
17383          * Recursively searches the immediate parent and all child nodes for
17384          * the handle element in order to determine wheter or not it was
17385          * clicked.
17386          * @method handleWasClicked
17387          * @param node the html element to inspect
17388          * @static
17389          */
17390         handleWasClicked: function(node, id) {
17391             if (this.isHandle(id, node.id)) {
17392                 return true;
17393             } else {
17394                 // check to see if this is a text node child of the one we want
17395                 var p = node.parentNode;
17396
17397                 while (p) {
17398                     if (this.isHandle(id, p.id)) {
17399                         return true;
17400                     } else {
17401                         p = p.parentNode;
17402                     }
17403                 }
17404             }
17405
17406             return false;
17407         }
17408
17409     };
17410
17411 }();
17412
17413 // shorter alias, save a few bytes
17414 Roo.dd.DDM = Roo.dd.DragDropMgr;
17415 Roo.dd.DDM._addListeners();
17416
17417 }/*
17418  * Based on:
17419  * Ext JS Library 1.1.1
17420  * Copyright(c) 2006-2007, Ext JS, LLC.
17421  *
17422  * Originally Released Under LGPL - original licence link has changed is not relivant.
17423  *
17424  * Fork - LGPL
17425  * <script type="text/javascript">
17426  */
17427
17428 /**
17429  * @class Roo.dd.DD
17430  * A DragDrop implementation where the linked element follows the
17431  * mouse cursor during a drag.
17432  * @extends Roo.dd.DragDrop
17433  * @constructor
17434  * @param {String} id the id of the linked element
17435  * @param {String} sGroup the group of related DragDrop items
17436  * @param {object} config an object containing configurable attributes
17437  *                Valid properties for DD:
17438  *                    scroll
17439  */
17440 Roo.dd.DD = function(id, sGroup, config) {
17441     if (id) {
17442         this.init(id, sGroup, config);
17443     }
17444 };
17445
17446 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
17447
17448     /**
17449      * When set to true, the utility automatically tries to scroll the browser
17450      * window wehn a drag and drop element is dragged near the viewport boundary.
17451      * Defaults to true.
17452      * @property scroll
17453      * @type boolean
17454      */
17455     scroll: true,
17456
17457     /**
17458      * Sets the pointer offset to the distance between the linked element's top
17459      * left corner and the location the element was clicked
17460      * @method autoOffset
17461      * @param {int} iPageX the X coordinate of the click
17462      * @param {int} iPageY the Y coordinate of the click
17463      */
17464     autoOffset: function(iPageX, iPageY) {
17465         var x = iPageX - this.startPageX;
17466         var y = iPageY - this.startPageY;
17467         this.setDelta(x, y);
17468     },
17469
17470     /**
17471      * Sets the pointer offset.  You can call this directly to force the
17472      * offset to be in a particular location (e.g., pass in 0,0 to set it
17473      * to the center of the object)
17474      * @method setDelta
17475      * @param {int} iDeltaX the distance from the left
17476      * @param {int} iDeltaY the distance from the top
17477      */
17478     setDelta: function(iDeltaX, iDeltaY) {
17479         this.deltaX = iDeltaX;
17480         this.deltaY = iDeltaY;
17481     },
17482
17483     /**
17484      * Sets the drag element to the location of the mousedown or click event,
17485      * maintaining the cursor location relative to the location on the element
17486      * that was clicked.  Override this if you want to place the element in a
17487      * location other than where the cursor is.
17488      * @method setDragElPos
17489      * @param {int} iPageX the X coordinate of the mousedown or drag event
17490      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17491      */
17492     setDragElPos: function(iPageX, iPageY) {
17493         // the first time we do this, we are going to check to make sure
17494         // the element has css positioning
17495
17496         var el = this.getDragEl();
17497         this.alignElWithMouse(el, iPageX, iPageY);
17498     },
17499
17500     /**
17501      * Sets the element to the location of the mousedown or click event,
17502      * maintaining the cursor location relative to the location on the element
17503      * that was clicked.  Override this if you want to place the element in a
17504      * location other than where the cursor is.
17505      * @method alignElWithMouse
17506      * @param {HTMLElement} el the element to move
17507      * @param {int} iPageX the X coordinate of the mousedown or drag event
17508      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17509      */
17510     alignElWithMouse: function(el, iPageX, iPageY) {
17511         var oCoord = this.getTargetCoord(iPageX, iPageY);
17512         var fly = el.dom ? el : Roo.fly(el);
17513         if (!this.deltaSetXY) {
17514             var aCoord = [oCoord.x, oCoord.y];
17515             fly.setXY(aCoord);
17516             var newLeft = fly.getLeft(true);
17517             var newTop  = fly.getTop(true);
17518             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17519         } else {
17520             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17521         }
17522
17523         this.cachePosition(oCoord.x, oCoord.y);
17524         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17525         return oCoord;
17526     },
17527
17528     /**
17529      * Saves the most recent position so that we can reset the constraints and
17530      * tick marks on-demand.  We need to know this so that we can calculate the
17531      * number of pixels the element is offset from its original position.
17532      * @method cachePosition
17533      * @param iPageX the current x position (optional, this just makes it so we
17534      * don't have to look it up again)
17535      * @param iPageY the current y position (optional, this just makes it so we
17536      * don't have to look it up again)
17537      */
17538     cachePosition: function(iPageX, iPageY) {
17539         if (iPageX) {
17540             this.lastPageX = iPageX;
17541             this.lastPageY = iPageY;
17542         } else {
17543             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17544             this.lastPageX = aCoord[0];
17545             this.lastPageY = aCoord[1];
17546         }
17547     },
17548
17549     /**
17550      * Auto-scroll the window if the dragged object has been moved beyond the
17551      * visible window boundary.
17552      * @method autoScroll
17553      * @param {int} x the drag element's x position
17554      * @param {int} y the drag element's y position
17555      * @param {int} h the height of the drag element
17556      * @param {int} w the width of the drag element
17557      * @private
17558      */
17559     autoScroll: function(x, y, h, w) {
17560
17561         if (this.scroll) {
17562             // The client height
17563             var clientH = Roo.lib.Dom.getViewWidth();
17564
17565             // The client width
17566             var clientW = Roo.lib.Dom.getViewHeight();
17567
17568             // The amt scrolled down
17569             var st = this.DDM.getScrollTop();
17570
17571             // The amt scrolled right
17572             var sl = this.DDM.getScrollLeft();
17573
17574             // Location of the bottom of the element
17575             var bot = h + y;
17576
17577             // Location of the right of the element
17578             var right = w + x;
17579
17580             // The distance from the cursor to the bottom of the visible area,
17581             // adjusted so that we don't scroll if the cursor is beyond the
17582             // element drag constraints
17583             var toBot = (clientH + st - y - this.deltaY);
17584
17585             // The distance from the cursor to the right of the visible area
17586             var toRight = (clientW + sl - x - this.deltaX);
17587
17588
17589             // How close to the edge the cursor must be before we scroll
17590             // var thresh = (document.all) ? 100 : 40;
17591             var thresh = 40;
17592
17593             // How many pixels to scroll per autoscroll op.  This helps to reduce
17594             // clunky scrolling. IE is more sensitive about this ... it needs this
17595             // value to be higher.
17596             var scrAmt = (document.all) ? 80 : 30;
17597
17598             // Scroll down if we are near the bottom of the visible page and the
17599             // obj extends below the crease
17600             if ( bot > clientH && toBot < thresh ) {
17601                 window.scrollTo(sl, st + scrAmt);
17602             }
17603
17604             // Scroll up if the window is scrolled down and the top of the object
17605             // goes above the top border
17606             if ( y < st && st > 0 && y - st < thresh ) {
17607                 window.scrollTo(sl, st - scrAmt);
17608             }
17609
17610             // Scroll right if the obj is beyond the right border and the cursor is
17611             // near the border.
17612             if ( right > clientW && toRight < thresh ) {
17613                 window.scrollTo(sl + scrAmt, st);
17614             }
17615
17616             // Scroll left if the window has been scrolled to the right and the obj
17617             // extends past the left border
17618             if ( x < sl && sl > 0 && x - sl < thresh ) {
17619                 window.scrollTo(sl - scrAmt, st);
17620             }
17621         }
17622     },
17623
17624     /**
17625      * Finds the location the element should be placed if we want to move
17626      * it to where the mouse location less the click offset would place us.
17627      * @method getTargetCoord
17628      * @param {int} iPageX the X coordinate of the click
17629      * @param {int} iPageY the Y coordinate of the click
17630      * @return an object that contains the coordinates (Object.x and Object.y)
17631      * @private
17632      */
17633     getTargetCoord: function(iPageX, iPageY) {
17634
17635
17636         var x = iPageX - this.deltaX;
17637         var y = iPageY - this.deltaY;
17638
17639         if (this.constrainX) {
17640             if (x < this.minX) { x = this.minX; }
17641             if (x > this.maxX) { x = this.maxX; }
17642         }
17643
17644         if (this.constrainY) {
17645             if (y < this.minY) { y = this.minY; }
17646             if (y > this.maxY) { y = this.maxY; }
17647         }
17648
17649         x = this.getTick(x, this.xTicks);
17650         y = this.getTick(y, this.yTicks);
17651
17652
17653         return {x:x, y:y};
17654     },
17655
17656     /*
17657      * Sets up config options specific to this class. Overrides
17658      * Roo.dd.DragDrop, but all versions of this method through the
17659      * inheritance chain are called
17660      */
17661     applyConfig: function() {
17662         Roo.dd.DD.superclass.applyConfig.call(this);
17663         this.scroll = (this.config.scroll !== false);
17664     },
17665
17666     /*
17667      * Event that fires prior to the onMouseDown event.  Overrides
17668      * Roo.dd.DragDrop.
17669      */
17670     b4MouseDown: function(e) {
17671         // this.resetConstraints();
17672         this.autoOffset(e.getPageX(),
17673                             e.getPageY());
17674     },
17675
17676     /*
17677      * Event that fires prior to the onDrag event.  Overrides
17678      * Roo.dd.DragDrop.
17679      */
17680     b4Drag: function(e) {
17681         this.setDragElPos(e.getPageX(),
17682                             e.getPageY());
17683     },
17684
17685     toString: function() {
17686         return ("DD " + this.id);
17687     }
17688
17689     //////////////////////////////////////////////////////////////////////////
17690     // Debugging ygDragDrop events that can be overridden
17691     //////////////////////////////////////////////////////////////////////////
17692     /*
17693     startDrag: function(x, y) {
17694     },
17695
17696     onDrag: function(e) {
17697     },
17698
17699     onDragEnter: function(e, id) {
17700     },
17701
17702     onDragOver: function(e, id) {
17703     },
17704
17705     onDragOut: function(e, id) {
17706     },
17707
17708     onDragDrop: function(e, id) {
17709     },
17710
17711     endDrag: function(e) {
17712     }
17713
17714     */
17715
17716 });/*
17717  * Based on:
17718  * Ext JS Library 1.1.1
17719  * Copyright(c) 2006-2007, Ext JS, LLC.
17720  *
17721  * Originally Released Under LGPL - original licence link has changed is not relivant.
17722  *
17723  * Fork - LGPL
17724  * <script type="text/javascript">
17725  */
17726
17727 /**
17728  * @class Roo.dd.DDProxy
17729  * A DragDrop implementation that inserts an empty, bordered div into
17730  * the document that follows the cursor during drag operations.  At the time of
17731  * the click, the frame div is resized to the dimensions of the linked html
17732  * element, and moved to the exact location of the linked element.
17733  *
17734  * References to the "frame" element refer to the single proxy element that
17735  * was created to be dragged in place of all DDProxy elements on the
17736  * page.
17737  *
17738  * @extends Roo.dd.DD
17739  * @constructor
17740  * @param {String} id the id of the linked html element
17741  * @param {String} sGroup the group of related DragDrop objects
17742  * @param {object} config an object containing configurable attributes
17743  *                Valid properties for DDProxy in addition to those in DragDrop:
17744  *                   resizeFrame, centerFrame, dragElId
17745  */
17746 Roo.dd.DDProxy = function(id, sGroup, config) {
17747     if (id) {
17748         this.init(id, sGroup, config);
17749         this.initFrame();
17750     }
17751 };
17752
17753 /**
17754  * The default drag frame div id
17755  * @property Roo.dd.DDProxy.dragElId
17756  * @type String
17757  * @static
17758  */
17759 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17760
17761 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17762
17763     /**
17764      * By default we resize the drag frame to be the same size as the element
17765      * we want to drag (this is to get the frame effect).  We can turn it off
17766      * if we want a different behavior.
17767      * @property resizeFrame
17768      * @type boolean
17769      */
17770     resizeFrame: true,
17771
17772     /**
17773      * By default the frame is positioned exactly where the drag element is, so
17774      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17775      * you do not have constraints on the obj is to have the drag frame centered
17776      * around the cursor.  Set centerFrame to true for this effect.
17777      * @property centerFrame
17778      * @type boolean
17779      */
17780     centerFrame: false,
17781
17782     /**
17783      * Creates the proxy element if it does not yet exist
17784      * @method createFrame
17785      */
17786     createFrame: function() {
17787         var self = this;
17788         var body = document.body;
17789
17790         if (!body || !body.firstChild) {
17791             setTimeout( function() { self.createFrame(); }, 50 );
17792             return;
17793         }
17794
17795         var div = this.getDragEl();
17796
17797         if (!div) {
17798             div    = document.createElement("div");
17799             div.id = this.dragElId;
17800             var s  = div.style;
17801
17802             s.position   = "absolute";
17803             s.visibility = "hidden";
17804             s.cursor     = "move";
17805             s.border     = "2px solid #aaa";
17806             s.zIndex     = 999;
17807
17808             // appendChild can blow up IE if invoked prior to the window load event
17809             // while rendering a table.  It is possible there are other scenarios
17810             // that would cause this to happen as well.
17811             body.insertBefore(div, body.firstChild);
17812         }
17813     },
17814
17815     /**
17816      * Initialization for the drag frame element.  Must be called in the
17817      * constructor of all subclasses
17818      * @method initFrame
17819      */
17820     initFrame: function() {
17821         this.createFrame();
17822     },
17823
17824     applyConfig: function() {
17825         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17826
17827         this.resizeFrame = (this.config.resizeFrame !== false);
17828         this.centerFrame = (this.config.centerFrame);
17829         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17830     },
17831
17832     /**
17833      * Resizes the drag frame to the dimensions of the clicked object, positions
17834      * it over the object, and finally displays it
17835      * @method showFrame
17836      * @param {int} iPageX X click position
17837      * @param {int} iPageY Y click position
17838      * @private
17839      */
17840     showFrame: function(iPageX, iPageY) {
17841         var el = this.getEl();
17842         var dragEl = this.getDragEl();
17843         var s = dragEl.style;
17844
17845         this._resizeProxy();
17846
17847         if (this.centerFrame) {
17848             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17849                            Math.round(parseInt(s.height, 10)/2) );
17850         }
17851
17852         this.setDragElPos(iPageX, iPageY);
17853
17854         Roo.fly(dragEl).show();
17855     },
17856
17857     /**
17858      * The proxy is automatically resized to the dimensions of the linked
17859      * element when a drag is initiated, unless resizeFrame is set to false
17860      * @method _resizeProxy
17861      * @private
17862      */
17863     _resizeProxy: function() {
17864         if (this.resizeFrame) {
17865             var el = this.getEl();
17866             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17867         }
17868     },
17869
17870     // overrides Roo.dd.DragDrop
17871     b4MouseDown: function(e) {
17872         var x = e.getPageX();
17873         var y = e.getPageY();
17874         this.autoOffset(x, y);
17875         this.setDragElPos(x, y);
17876     },
17877
17878     // overrides Roo.dd.DragDrop
17879     b4StartDrag: function(x, y) {
17880         // show the drag frame
17881         this.showFrame(x, y);
17882     },
17883
17884     // overrides Roo.dd.DragDrop
17885     b4EndDrag: function(e) {
17886         Roo.fly(this.getDragEl()).hide();
17887     },
17888
17889     // overrides Roo.dd.DragDrop
17890     // By default we try to move the element to the last location of the frame.
17891     // This is so that the default behavior mirrors that of Roo.dd.DD.
17892     endDrag: function(e) {
17893
17894         var lel = this.getEl();
17895         var del = this.getDragEl();
17896
17897         // Show the drag frame briefly so we can get its position
17898         del.style.visibility = "";
17899
17900         this.beforeMove();
17901         // Hide the linked element before the move to get around a Safari
17902         // rendering bug.
17903         lel.style.visibility = "hidden";
17904         Roo.dd.DDM.moveToEl(lel, del);
17905         del.style.visibility = "hidden";
17906         lel.style.visibility = "";
17907
17908         this.afterDrag();
17909     },
17910
17911     beforeMove : function(){
17912
17913     },
17914
17915     afterDrag : function(){
17916
17917     },
17918
17919     toString: function() {
17920         return ("DDProxy " + this.id);
17921     }
17922
17923 });
17924 /*
17925  * Based on:
17926  * Ext JS Library 1.1.1
17927  * Copyright(c) 2006-2007, Ext JS, LLC.
17928  *
17929  * Originally Released Under LGPL - original licence link has changed is not relivant.
17930  *
17931  * Fork - LGPL
17932  * <script type="text/javascript">
17933  */
17934
17935  /**
17936  * @class Roo.dd.DDTarget
17937  * A DragDrop implementation that does not move, but can be a drop
17938  * target.  You would get the same result by simply omitting implementation
17939  * for the event callbacks, but this way we reduce the processing cost of the
17940  * event listener and the callbacks.
17941  * @extends Roo.dd.DragDrop
17942  * @constructor
17943  * @param {String} id the id of the element that is a drop target
17944  * @param {String} sGroup the group of related DragDrop objects
17945  * @param {object} config an object containing configurable attributes
17946  *                 Valid properties for DDTarget in addition to those in
17947  *                 DragDrop:
17948  *                    none
17949  */
17950 Roo.dd.DDTarget = function(id, sGroup, config) {
17951     if (id) {
17952         this.initTarget(id, sGroup, config);
17953     }
17954     if (config.listeners || config.events) { 
17955        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17956             listeners : config.listeners || {}, 
17957             events : config.events || {} 
17958         });    
17959     }
17960 };
17961
17962 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17963 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17964     toString: function() {
17965         return ("DDTarget " + this.id);
17966     }
17967 });
17968 /*
17969  * Based on:
17970  * Ext JS Library 1.1.1
17971  * Copyright(c) 2006-2007, Ext JS, LLC.
17972  *
17973  * Originally Released Under LGPL - original licence link has changed is not relivant.
17974  *
17975  * Fork - LGPL
17976  * <script type="text/javascript">
17977  */
17978  
17979
17980 /**
17981  * @class Roo.dd.ScrollManager
17982  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17983  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17984  * @singleton
17985  */
17986 Roo.dd.ScrollManager = function(){
17987     var ddm = Roo.dd.DragDropMgr;
17988     var els = {};
17989     var dragEl = null;
17990     var proc = {};
17991     
17992     
17993     
17994     var onStop = function(e){
17995         dragEl = null;
17996         clearProc();
17997     };
17998     
17999     var triggerRefresh = function(){
18000         if(ddm.dragCurrent){
18001              ddm.refreshCache(ddm.dragCurrent.groups);
18002         }
18003     };
18004     
18005     var doScroll = function(){
18006         if(ddm.dragCurrent){
18007             var dds = Roo.dd.ScrollManager;
18008             if(!dds.animate){
18009                 if(proc.el.scroll(proc.dir, dds.increment)){
18010                     triggerRefresh();
18011                 }
18012             }else{
18013                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
18014             }
18015         }
18016     };
18017     
18018     var clearProc = function(){
18019         if(proc.id){
18020             clearInterval(proc.id);
18021         }
18022         proc.id = 0;
18023         proc.el = null;
18024         proc.dir = "";
18025     };
18026     
18027     var startProc = function(el, dir){
18028          Roo.log('scroll startproc');
18029         clearProc();
18030         proc.el = el;
18031         proc.dir = dir;
18032         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
18033     };
18034     
18035     var onFire = function(e, isDrop){
18036        
18037         if(isDrop || !ddm.dragCurrent){ return; }
18038         var dds = Roo.dd.ScrollManager;
18039         if(!dragEl || dragEl != ddm.dragCurrent){
18040             dragEl = ddm.dragCurrent;
18041             // refresh regions on drag start
18042             dds.refreshCache();
18043         }
18044         
18045         var xy = Roo.lib.Event.getXY(e);
18046         var pt = new Roo.lib.Point(xy[0], xy[1]);
18047         for(var id in els){
18048             var el = els[id], r = el._region;
18049             if(r && r.contains(pt) && el.isScrollable()){
18050                 if(r.bottom - pt.y <= dds.thresh){
18051                     if(proc.el != el){
18052                         startProc(el, "down");
18053                     }
18054                     return;
18055                 }else if(r.right - pt.x <= dds.thresh){
18056                     if(proc.el != el){
18057                         startProc(el, "left");
18058                     }
18059                     return;
18060                 }else if(pt.y - r.top <= dds.thresh){
18061                     if(proc.el != el){
18062                         startProc(el, "up");
18063                     }
18064                     return;
18065                 }else if(pt.x - r.left <= dds.thresh){
18066                     if(proc.el != el){
18067                         startProc(el, "right");
18068                     }
18069                     return;
18070                 }
18071             }
18072         }
18073         clearProc();
18074     };
18075     
18076     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
18077     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
18078     
18079     return {
18080         /**
18081          * Registers new overflow element(s) to auto scroll
18082          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
18083          */
18084         register : function(el){
18085             if(el instanceof Array){
18086                 for(var i = 0, len = el.length; i < len; i++) {
18087                         this.register(el[i]);
18088                 }
18089             }else{
18090                 el = Roo.get(el);
18091                 els[el.id] = el;
18092             }
18093             Roo.dd.ScrollManager.els = els;
18094         },
18095         
18096         /**
18097          * Unregisters overflow element(s) so they are no longer scrolled
18098          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
18099          */
18100         unregister : function(el){
18101             if(el instanceof Array){
18102                 for(var i = 0, len = el.length; i < len; i++) {
18103                         this.unregister(el[i]);
18104                 }
18105             }else{
18106                 el = Roo.get(el);
18107                 delete els[el.id];
18108             }
18109         },
18110         
18111         /**
18112          * The number of pixels from the edge of a container the pointer needs to be to 
18113          * trigger scrolling (defaults to 25)
18114          * @type Number
18115          */
18116         thresh : 25,
18117         
18118         /**
18119          * The number of pixels to scroll in each scroll increment (defaults to 50)
18120          * @type Number
18121          */
18122         increment : 100,
18123         
18124         /**
18125          * The frequency of scrolls in milliseconds (defaults to 500)
18126          * @type Number
18127          */
18128         frequency : 500,
18129         
18130         /**
18131          * True to animate the scroll (defaults to true)
18132          * @type Boolean
18133          */
18134         animate: true,
18135         
18136         /**
18137          * The animation duration in seconds - 
18138          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
18139          * @type Number
18140          */
18141         animDuration: .4,
18142         
18143         /**
18144          * Manually trigger a cache refresh.
18145          */
18146         refreshCache : function(){
18147             for(var id in els){
18148                 if(typeof els[id] == 'object'){ // for people extending the object prototype
18149                     els[id]._region = els[id].getRegion();
18150                 }
18151             }
18152         }
18153     };
18154 }();/*
18155  * Based on:
18156  * Ext JS Library 1.1.1
18157  * Copyright(c) 2006-2007, Ext JS, LLC.
18158  *
18159  * Originally Released Under LGPL - original licence link has changed is not relivant.
18160  *
18161  * Fork - LGPL
18162  * <script type="text/javascript">
18163  */
18164  
18165
18166 /**
18167  * @class Roo.dd.Registry
18168  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
18169  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
18170  * @singleton
18171  */
18172 Roo.dd.Registry = function(){
18173     var elements = {}; 
18174     var handles = {}; 
18175     var autoIdSeed = 0;
18176
18177     var getId = function(el, autogen){
18178         if(typeof el == "string"){
18179             return el;
18180         }
18181         var id = el.id;
18182         if(!id && autogen !== false){
18183             id = "roodd-" + (++autoIdSeed);
18184             el.id = id;
18185         }
18186         return id;
18187     };
18188     
18189     return {
18190     /**
18191      * Register a drag drop element
18192      * @param {String|HTMLElement} element The id or DOM node to register
18193      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
18194      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
18195      * knows how to interpret, plus there are some specific properties known to the Registry that should be
18196      * populated in the data object (if applicable):
18197      * <pre>
18198 Value      Description<br />
18199 ---------  ------------------------------------------<br />
18200 handles    Array of DOM nodes that trigger dragging<br />
18201            for the element being registered<br />
18202 isHandle   True if the element passed in triggers<br />
18203            dragging itself, else false
18204 </pre>
18205      */
18206         register : function(el, data){
18207             data = data || {};
18208             if(typeof el == "string"){
18209                 el = document.getElementById(el);
18210             }
18211             data.ddel = el;
18212             elements[getId(el)] = data;
18213             if(data.isHandle !== false){
18214                 handles[data.ddel.id] = data;
18215             }
18216             if(data.handles){
18217                 var hs = data.handles;
18218                 for(var i = 0, len = hs.length; i < len; i++){
18219                         handles[getId(hs[i])] = data;
18220                 }
18221             }
18222         },
18223
18224     /**
18225      * Unregister a drag drop element
18226      * @param {String|HTMLElement}  element The id or DOM node to unregister
18227      */
18228         unregister : function(el){
18229             var id = getId(el, false);
18230             var data = elements[id];
18231             if(data){
18232                 delete elements[id];
18233                 if(data.handles){
18234                     var hs = data.handles;
18235                     for(var i = 0, len = hs.length; i < len; i++){
18236                         delete handles[getId(hs[i], false)];
18237                     }
18238                 }
18239             }
18240         },
18241
18242     /**
18243      * Returns the handle registered for a DOM Node by id
18244      * @param {String|HTMLElement} id The DOM node or id to look up
18245      * @return {Object} handle The custom handle data
18246      */
18247         getHandle : function(id){
18248             if(typeof id != "string"){ // must be element?
18249                 id = id.id;
18250             }
18251             return handles[id];
18252         },
18253
18254     /**
18255      * Returns the handle that is registered for the DOM node that is the target of the event
18256      * @param {Event} e The event
18257      * @return {Object} handle The custom handle data
18258      */
18259         getHandleFromEvent : function(e){
18260             var t = Roo.lib.Event.getTarget(e);
18261             return t ? handles[t.id] : null;
18262         },
18263
18264     /**
18265      * Returns a custom data object that is registered for a DOM node by id
18266      * @param {String|HTMLElement} id The DOM node or id to look up
18267      * @return {Object} data The custom data
18268      */
18269         getTarget : function(id){
18270             if(typeof id != "string"){ // must be element?
18271                 id = id.id;
18272             }
18273             return elements[id];
18274         },
18275
18276     /**
18277      * Returns a custom data object that is registered for the DOM node that is the target of the event
18278      * @param {Event} e The event
18279      * @return {Object} data The custom data
18280      */
18281         getTargetFromEvent : function(e){
18282             var t = Roo.lib.Event.getTarget(e);
18283             return t ? elements[t.id] || handles[t.id] : null;
18284         }
18285     };
18286 }();/*
18287  * Based on:
18288  * Ext JS Library 1.1.1
18289  * Copyright(c) 2006-2007, Ext JS, LLC.
18290  *
18291  * Originally Released Under LGPL - original licence link has changed is not relivant.
18292  *
18293  * Fork - LGPL
18294  * <script type="text/javascript">
18295  */
18296  
18297
18298 /**
18299  * @class Roo.dd.StatusProxy
18300  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
18301  * default drag proxy used by all Roo.dd components.
18302  * @constructor
18303  * @param {Object} config
18304  */
18305 Roo.dd.StatusProxy = function(config){
18306     Roo.apply(this, config);
18307     this.id = this.id || Roo.id();
18308     this.el = new Roo.Layer({
18309         dh: {
18310             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
18311                 {tag: "div", cls: "x-dd-drop-icon"},
18312                 {tag: "div", cls: "x-dd-drag-ghost"}
18313             ]
18314         }, 
18315         shadow: !config || config.shadow !== false
18316     });
18317     this.ghost = Roo.get(this.el.dom.childNodes[1]);
18318     this.dropStatus = this.dropNotAllowed;
18319 };
18320
18321 Roo.dd.StatusProxy.prototype = {
18322     /**
18323      * @cfg {String} dropAllowed
18324      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
18325      */
18326     dropAllowed : "x-dd-drop-ok",
18327     /**
18328      * @cfg {String} dropNotAllowed
18329      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
18330      */
18331     dropNotAllowed : "x-dd-drop-nodrop",
18332
18333     /**
18334      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
18335      * over the current target element.
18336      * @param {String} cssClass The css class for the new drop status indicator image
18337      */
18338     setStatus : function(cssClass){
18339         cssClass = cssClass || this.dropNotAllowed;
18340         if(this.dropStatus != cssClass){
18341             this.el.replaceClass(this.dropStatus, cssClass);
18342             this.dropStatus = cssClass;
18343         }
18344     },
18345
18346     /**
18347      * Resets the status indicator to the default dropNotAllowed value
18348      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
18349      */
18350     reset : function(clearGhost){
18351         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
18352         this.dropStatus = this.dropNotAllowed;
18353         if(clearGhost){
18354             this.ghost.update("");
18355         }
18356     },
18357
18358     /**
18359      * Updates the contents of the ghost element
18360      * @param {String} html The html that will replace the current innerHTML of the ghost element
18361      */
18362     update : function(html){
18363         if(typeof html == "string"){
18364             this.ghost.update(html);
18365         }else{
18366             this.ghost.update("");
18367             html.style.margin = "0";
18368             this.ghost.dom.appendChild(html);
18369         }
18370         // ensure float = none set?? cant remember why though.
18371         var el = this.ghost.dom.firstChild;
18372                 if(el){
18373                         Roo.fly(el).setStyle('float', 'none');
18374                 }
18375     },
18376     
18377     /**
18378      * Returns the underlying proxy {@link Roo.Layer}
18379      * @return {Roo.Layer} el
18380     */
18381     getEl : function(){
18382         return this.el;
18383     },
18384
18385     /**
18386      * Returns the ghost element
18387      * @return {Roo.Element} el
18388      */
18389     getGhost : function(){
18390         return this.ghost;
18391     },
18392
18393     /**
18394      * Hides the proxy
18395      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
18396      */
18397     hide : function(clear){
18398         this.el.hide();
18399         if(clear){
18400             this.reset(true);
18401         }
18402     },
18403
18404     /**
18405      * Stops the repair animation if it's currently running
18406      */
18407     stop : function(){
18408         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
18409             this.anim.stop();
18410         }
18411     },
18412
18413     /**
18414      * Displays this proxy
18415      */
18416     show : function(){
18417         this.el.show();
18418     },
18419
18420     /**
18421      * Force the Layer to sync its shadow and shim positions to the element
18422      */
18423     sync : function(){
18424         this.el.sync();
18425     },
18426
18427     /**
18428      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
18429      * invalid drop operation by the item being dragged.
18430      * @param {Array} xy The XY position of the element ([x, y])
18431      * @param {Function} callback The function to call after the repair is complete
18432      * @param {Object} scope The scope in which to execute the callback
18433      */
18434     repair : function(xy, callback, scope){
18435         this.callback = callback;
18436         this.scope = scope;
18437         if(xy && this.animRepair !== false){
18438             this.el.addClass("x-dd-drag-repair");
18439             this.el.hideUnders(true);
18440             this.anim = this.el.shift({
18441                 duration: this.repairDuration || .5,
18442                 easing: 'easeOut',
18443                 xy: xy,
18444                 stopFx: true,
18445                 callback: this.afterRepair,
18446                 scope: this
18447             });
18448         }else{
18449             this.afterRepair();
18450         }
18451     },
18452
18453     // private
18454     afterRepair : function(){
18455         this.hide(true);
18456         if(typeof this.callback == "function"){
18457             this.callback.call(this.scope || this);
18458         }
18459         this.callback = null;
18460         this.scope = null;
18461     }
18462 };/*
18463  * Based on:
18464  * Ext JS Library 1.1.1
18465  * Copyright(c) 2006-2007, Ext JS, LLC.
18466  *
18467  * Originally Released Under LGPL - original licence link has changed is not relivant.
18468  *
18469  * Fork - LGPL
18470  * <script type="text/javascript">
18471  */
18472
18473 /**
18474  * @class Roo.dd.DragSource
18475  * @extends Roo.dd.DDProxy
18476  * A simple class that provides the basic implementation needed to make any element draggable.
18477  * @constructor
18478  * @param {String/HTMLElement/Element} el The container element
18479  * @param {Object} config
18480  */
18481 Roo.dd.DragSource = function(el, config){
18482     this.el = Roo.get(el);
18483     this.dragData = {};
18484     
18485     Roo.apply(this, config);
18486     
18487     if(!this.proxy){
18488         this.proxy = new Roo.dd.StatusProxy();
18489     }
18490
18491     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18492           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18493     
18494     this.dragging = false;
18495 };
18496
18497 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18498     /**
18499      * @cfg {String} dropAllowed
18500      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18501      */
18502     dropAllowed : "x-dd-drop-ok",
18503     /**
18504      * @cfg {String} dropNotAllowed
18505      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18506      */
18507     dropNotAllowed : "x-dd-drop-nodrop",
18508
18509     /**
18510      * Returns the data object associated with this drag source
18511      * @return {Object} data An object containing arbitrary data
18512      */
18513     getDragData : function(e){
18514         return this.dragData;
18515     },
18516
18517     // private
18518     onDragEnter : function(e, id){
18519         var target = Roo.dd.DragDropMgr.getDDById(id);
18520         this.cachedTarget = target;
18521         if(this.beforeDragEnter(target, e, id) !== false){
18522             if(target.isNotifyTarget){
18523                 var status = target.notifyEnter(this, e, this.dragData);
18524                 this.proxy.setStatus(status);
18525             }else{
18526                 this.proxy.setStatus(this.dropAllowed);
18527             }
18528             
18529             if(this.afterDragEnter){
18530                 /**
18531                  * An empty function by default, but provided so that you can perform a custom action
18532                  * when the dragged item enters the drop target by providing an implementation.
18533                  * @param {Roo.dd.DragDrop} target The drop target
18534                  * @param {Event} e The event object
18535                  * @param {String} id The id of the dragged element
18536                  * @method afterDragEnter
18537                  */
18538                 this.afterDragEnter(target, e, id);
18539             }
18540         }
18541     },
18542
18543     /**
18544      * An empty function by default, but provided so that you can perform a custom action
18545      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18546      * @param {Roo.dd.DragDrop} target The drop target
18547      * @param {Event} e The event object
18548      * @param {String} id The id of the dragged element
18549      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18550      */
18551     beforeDragEnter : function(target, e, id){
18552         return true;
18553     },
18554
18555     // private
18556     alignElWithMouse: function() {
18557         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18558         this.proxy.sync();
18559     },
18560
18561     // private
18562     onDragOver : function(e, id){
18563         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18564         if(this.beforeDragOver(target, e, id) !== false){
18565             if(target.isNotifyTarget){
18566                 var status = target.notifyOver(this, e, this.dragData);
18567                 this.proxy.setStatus(status);
18568             }
18569
18570             if(this.afterDragOver){
18571                 /**
18572                  * An empty function by default, but provided so that you can perform a custom action
18573                  * while the dragged item is over the drop target by providing an implementation.
18574                  * @param {Roo.dd.DragDrop} target The drop target
18575                  * @param {Event} e The event object
18576                  * @param {String} id The id of the dragged element
18577                  * @method afterDragOver
18578                  */
18579                 this.afterDragOver(target, e, id);
18580             }
18581         }
18582     },
18583
18584     /**
18585      * An empty function by default, but provided so that you can perform a custom action
18586      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18587      * @param {Roo.dd.DragDrop} target The drop target
18588      * @param {Event} e The event object
18589      * @param {String} id The id of the dragged element
18590      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18591      */
18592     beforeDragOver : function(target, e, id){
18593         return true;
18594     },
18595
18596     // private
18597     onDragOut : function(e, id){
18598         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18599         if(this.beforeDragOut(target, e, id) !== false){
18600             if(target.isNotifyTarget){
18601                 target.notifyOut(this, e, this.dragData);
18602             }
18603             this.proxy.reset();
18604             if(this.afterDragOut){
18605                 /**
18606                  * An empty function by default, but provided so that you can perform a custom action
18607                  * after the dragged item is dragged out of the target without dropping.
18608                  * @param {Roo.dd.DragDrop} target The drop target
18609                  * @param {Event} e The event object
18610                  * @param {String} id The id of the dragged element
18611                  * @method afterDragOut
18612                  */
18613                 this.afterDragOut(target, e, id);
18614             }
18615         }
18616         this.cachedTarget = null;
18617     },
18618
18619     /**
18620      * An empty function by default, but provided so that you can perform a custom action before the dragged
18621      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18622      * @param {Roo.dd.DragDrop} target The drop target
18623      * @param {Event} e The event object
18624      * @param {String} id The id of the dragged element
18625      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18626      */
18627     beforeDragOut : function(target, e, id){
18628         return true;
18629     },
18630     
18631     // private
18632     onDragDrop : function(e, id){
18633         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18634         if(this.beforeDragDrop(target, e, id) !== false){
18635             if(target.isNotifyTarget){
18636                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18637                     this.onValidDrop(target, e, id);
18638                 }else{
18639                     this.onInvalidDrop(target, e, id);
18640                 }
18641             }else{
18642                 this.onValidDrop(target, e, id);
18643             }
18644             
18645             if(this.afterDragDrop){
18646                 /**
18647                  * An empty function by default, but provided so that you can perform a custom action
18648                  * after a valid drag drop has occurred by providing an implementation.
18649                  * @param {Roo.dd.DragDrop} target The drop target
18650                  * @param {Event} e The event object
18651                  * @param {String} id The id of the dropped element
18652                  * @method afterDragDrop
18653                  */
18654                 this.afterDragDrop(target, e, id);
18655             }
18656         }
18657         delete this.cachedTarget;
18658     },
18659
18660     /**
18661      * An empty function by default, but provided so that you can perform a custom action before the dragged
18662      * item is dropped onto the target and optionally cancel the onDragDrop.
18663      * @param {Roo.dd.DragDrop} target The drop target
18664      * @param {Event} e The event object
18665      * @param {String} id The id of the dragged element
18666      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18667      */
18668     beforeDragDrop : function(target, e, id){
18669         return true;
18670     },
18671
18672     // private
18673     onValidDrop : function(target, e, id){
18674         this.hideProxy();
18675         if(this.afterValidDrop){
18676             /**
18677              * An empty function by default, but provided so that you can perform a custom action
18678              * after a valid drop has occurred by providing an implementation.
18679              * @param {Object} target The target DD 
18680              * @param {Event} e The event object
18681              * @param {String} id The id of the dropped element
18682              * @method afterInvalidDrop
18683              */
18684             this.afterValidDrop(target, e, id);
18685         }
18686     },
18687
18688     // private
18689     getRepairXY : function(e, data){
18690         return this.el.getXY();  
18691     },
18692
18693     // private
18694     onInvalidDrop : function(target, e, id){
18695         this.beforeInvalidDrop(target, e, id);
18696         if(this.cachedTarget){
18697             if(this.cachedTarget.isNotifyTarget){
18698                 this.cachedTarget.notifyOut(this, e, this.dragData);
18699             }
18700             this.cacheTarget = null;
18701         }
18702         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18703
18704         if(this.afterInvalidDrop){
18705             /**
18706              * An empty function by default, but provided so that you can perform a custom action
18707              * after an invalid drop has occurred by providing an implementation.
18708              * @param {Event} e The event object
18709              * @param {String} id The id of the dropped element
18710              * @method afterInvalidDrop
18711              */
18712             this.afterInvalidDrop(e, id);
18713         }
18714     },
18715
18716     // private
18717     afterRepair : function(){
18718         if(Roo.enableFx){
18719             this.el.highlight(this.hlColor || "c3daf9");
18720         }
18721         this.dragging = false;
18722     },
18723
18724     /**
18725      * An empty function by default, but provided so that you can perform a custom action after an invalid
18726      * drop has occurred.
18727      * @param {Roo.dd.DragDrop} target The drop target
18728      * @param {Event} e The event object
18729      * @param {String} id The id of the dragged element
18730      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18731      */
18732     beforeInvalidDrop : function(target, e, id){
18733         return true;
18734     },
18735
18736     // private
18737     handleMouseDown : function(e){
18738         if(this.dragging) {
18739             return;
18740         }
18741         var data = this.getDragData(e);
18742         if(data && this.onBeforeDrag(data, e) !== false){
18743             this.dragData = data;
18744             this.proxy.stop();
18745             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18746         } 
18747     },
18748
18749     /**
18750      * An empty function by default, but provided so that you can perform a custom action before the initial
18751      * drag event begins and optionally cancel it.
18752      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18753      * @param {Event} e The event object
18754      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18755      */
18756     onBeforeDrag : function(data, e){
18757         return true;
18758     },
18759
18760     /**
18761      * An empty function by default, but provided so that you can perform a custom action once the initial
18762      * drag event has begun.  The drag cannot be canceled from this function.
18763      * @param {Number} x The x position of the click on the dragged object
18764      * @param {Number} y The y position of the click on the dragged object
18765      */
18766     onStartDrag : Roo.emptyFn,
18767
18768     // private - YUI override
18769     startDrag : function(x, y){
18770         this.proxy.reset();
18771         this.dragging = true;
18772         this.proxy.update("");
18773         this.onInitDrag(x, y);
18774         this.proxy.show();
18775     },
18776
18777     // private
18778     onInitDrag : function(x, y){
18779         var clone = this.el.dom.cloneNode(true);
18780         clone.id = Roo.id(); // prevent duplicate ids
18781         this.proxy.update(clone);
18782         this.onStartDrag(x, y);
18783         return true;
18784     },
18785
18786     /**
18787      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18788      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18789      */
18790     getProxy : function(){
18791         return this.proxy;  
18792     },
18793
18794     /**
18795      * Hides the drag source's {@link Roo.dd.StatusProxy}
18796      */
18797     hideProxy : function(){
18798         this.proxy.hide();  
18799         this.proxy.reset(true);
18800         this.dragging = false;
18801     },
18802
18803     // private
18804     triggerCacheRefresh : function(){
18805         Roo.dd.DDM.refreshCache(this.groups);
18806     },
18807
18808     // private - override to prevent hiding
18809     b4EndDrag: function(e) {
18810     },
18811
18812     // private - override to prevent moving
18813     endDrag : function(e){
18814         this.onEndDrag(this.dragData, e);
18815     },
18816
18817     // private
18818     onEndDrag : function(data, e){
18819     },
18820     
18821     // private - pin to cursor
18822     autoOffset : function(x, y) {
18823         this.setDelta(-12, -20);
18824     }    
18825 });/*
18826  * Based on:
18827  * Ext JS Library 1.1.1
18828  * Copyright(c) 2006-2007, Ext JS, LLC.
18829  *
18830  * Originally Released Under LGPL - original licence link has changed is not relivant.
18831  *
18832  * Fork - LGPL
18833  * <script type="text/javascript">
18834  */
18835
18836
18837 /**
18838  * @class Roo.dd.DropTarget
18839  * @extends Roo.dd.DDTarget
18840  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18841  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18842  * @constructor
18843  * @param {String/HTMLElement/Element} el The container element
18844  * @param {Object} config
18845  */
18846 Roo.dd.DropTarget = function(el, config){
18847     this.el = Roo.get(el);
18848     
18849     var listeners = false; ;
18850     if (config && config.listeners) {
18851         listeners= config.listeners;
18852         delete config.listeners;
18853     }
18854     Roo.apply(this, config);
18855     
18856     if(this.containerScroll){
18857         Roo.dd.ScrollManager.register(this.el);
18858     }
18859     this.addEvents( {
18860          /**
18861          * @scope Roo.dd.DropTarget
18862          */
18863          
18864          /**
18865          * @event enter
18866          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18867          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18868          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18869          * 
18870          * IMPORTANT : it should set this.overClass and this.dropAllowed
18871          * 
18872          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18873          * @param {Event} e The event
18874          * @param {Object} data An object containing arbitrary data supplied by the drag source
18875          */
18876         "enter" : true,
18877         
18878          /**
18879          * @event over
18880          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18881          * This method will be called on every mouse movement while the drag source is over the drop target.
18882          * This default implementation simply returns the dropAllowed config value.
18883          * 
18884          * IMPORTANT : it should set this.dropAllowed
18885          * 
18886          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18887          * @param {Event} e The event
18888          * @param {Object} data An object containing arbitrary data supplied by the drag source
18889          
18890          */
18891         "over" : true,
18892         /**
18893          * @event out
18894          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18895          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18896          * overClass (if any) from the drop element.
18897          * 
18898          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18899          * @param {Event} e The event
18900          * @param {Object} data An object containing arbitrary data supplied by the drag source
18901          */
18902          "out" : true,
18903          
18904         /**
18905          * @event drop
18906          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18907          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18908          * implementation that does something to process the drop event and returns true so that the drag source's
18909          * repair action does not run.
18910          * 
18911          * IMPORTANT : it should set this.success
18912          * 
18913          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18914          * @param {Event} e The event
18915          * @param {Object} data An object containing arbitrary data supplied by the drag source
18916         */
18917          "drop" : true
18918     });
18919             
18920      
18921     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18922         this.el.dom, 
18923         this.ddGroup || this.group,
18924         {
18925             isTarget: true,
18926             listeners : listeners || {} 
18927            
18928         
18929         }
18930     );
18931
18932 };
18933
18934 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18935     /**
18936      * @cfg {String} overClass
18937      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18938      */
18939      /**
18940      * @cfg {String} ddGroup
18941      * The drag drop group to handle drop events for
18942      */
18943      
18944     /**
18945      * @cfg {String} dropAllowed
18946      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18947      */
18948     dropAllowed : "x-dd-drop-ok",
18949     /**
18950      * @cfg {String} dropNotAllowed
18951      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18952      */
18953     dropNotAllowed : "x-dd-drop-nodrop",
18954     /**
18955      * @cfg {boolean} success
18956      * set this after drop listener.. 
18957      */
18958     success : false,
18959     /**
18960      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18961      * if the drop point is valid for over/enter..
18962      */
18963     valid : false,
18964     // private
18965     isTarget : true,
18966
18967     // private
18968     isNotifyTarget : true,
18969     
18970     /**
18971      * @hide
18972      */
18973     notifyEnter : function(dd, e, data)
18974     {
18975         this.valid = true;
18976         this.fireEvent('enter', dd, e, data);
18977         if(this.overClass){
18978             this.el.addClass(this.overClass);
18979         }
18980         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18981             this.valid ? this.dropAllowed : this.dropNotAllowed
18982         );
18983     },
18984
18985     /**
18986      * @hide
18987      */
18988     notifyOver : function(dd, e, data)
18989     {
18990         this.valid = true;
18991         this.fireEvent('over', dd, e, data);
18992         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18993             this.valid ? this.dropAllowed : this.dropNotAllowed
18994         );
18995     },
18996
18997     /**
18998      * @hide
18999      */
19000     notifyOut : function(dd, e, data)
19001     {
19002         this.fireEvent('out', dd, e, data);
19003         if(this.overClass){
19004             this.el.removeClass(this.overClass);
19005         }
19006     },
19007
19008     /**
19009      * @hide
19010      */
19011     notifyDrop : function(dd, e, data)
19012     {
19013         this.success = false;
19014         this.fireEvent('drop', dd, e, data);
19015         return this.success;
19016     }
19017 });/*
19018  * Based on:
19019  * Ext JS Library 1.1.1
19020  * Copyright(c) 2006-2007, Ext JS, LLC.
19021  *
19022  * Originally Released Under LGPL - original licence link has changed is not relivant.
19023  *
19024  * Fork - LGPL
19025  * <script type="text/javascript">
19026  */
19027
19028
19029 /**
19030  * @class Roo.dd.DragZone
19031  * @extends Roo.dd.DragSource
19032  * This class provides a container DD instance that proxies for multiple child node sources.<br />
19033  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
19034  * @constructor
19035  * @param {String/HTMLElement/Element} el The container element
19036  * @param {Object} config
19037  */
19038 Roo.dd.DragZone = function(el, config){
19039     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
19040     if(this.containerScroll){
19041         Roo.dd.ScrollManager.register(this.el);
19042     }
19043 };
19044
19045 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
19046     /**
19047      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
19048      * for auto scrolling during drag operations.
19049      */
19050     /**
19051      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
19052      * method after a failed drop (defaults to "c3daf9" - light blue)
19053      */
19054
19055     /**
19056      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
19057      * for a valid target to drag based on the mouse down. Override this method
19058      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
19059      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
19060      * @param {EventObject} e The mouse down event
19061      * @return {Object} The dragData
19062      */
19063     getDragData : function(e){
19064         return Roo.dd.Registry.getHandleFromEvent(e);
19065     },
19066     
19067     /**
19068      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
19069      * this.dragData.ddel
19070      * @param {Number} x The x position of the click on the dragged object
19071      * @param {Number} y The y position of the click on the dragged object
19072      * @return {Boolean} true to continue the drag, false to cancel
19073      */
19074     onInitDrag : function(x, y){
19075         this.proxy.update(this.dragData.ddel.cloneNode(true));
19076         this.onStartDrag(x, y);
19077         return true;
19078     },
19079     
19080     /**
19081      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
19082      */
19083     afterRepair : function(){
19084         if(Roo.enableFx){
19085             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
19086         }
19087         this.dragging = false;
19088     },
19089
19090     /**
19091      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
19092      * the XY of this.dragData.ddel
19093      * @param {EventObject} e The mouse up event
19094      * @return {Array} The xy location (e.g. [100, 200])
19095      */
19096     getRepairXY : function(e){
19097         return Roo.Element.fly(this.dragData.ddel).getXY();  
19098     }
19099 });/*
19100  * Based on:
19101  * Ext JS Library 1.1.1
19102  * Copyright(c) 2006-2007, Ext JS, LLC.
19103  *
19104  * Originally Released Under LGPL - original licence link has changed is not relivant.
19105  *
19106  * Fork - LGPL
19107  * <script type="text/javascript">
19108  */
19109 /**
19110  * @class Roo.dd.DropZone
19111  * @extends Roo.dd.DropTarget
19112  * This class provides a container DD instance that proxies for multiple child node targets.<br />
19113  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
19114  * @constructor
19115  * @param {String/HTMLElement/Element} el The container element
19116  * @param {Object} config
19117  */
19118 Roo.dd.DropZone = function(el, config){
19119     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
19120 };
19121
19122 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
19123     /**
19124      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
19125      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
19126      * provide your own custom lookup.
19127      * @param {Event} e The event
19128      * @return {Object} data The custom data
19129      */
19130     getTargetFromEvent : function(e){
19131         return Roo.dd.Registry.getTargetFromEvent(e);
19132     },
19133
19134     /**
19135      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
19136      * that it has registered.  This method has no default implementation and should be overridden to provide
19137      * node-specific processing if necessary.
19138      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
19139      * {@link #getTargetFromEvent} for this node)
19140      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19141      * @param {Event} e The event
19142      * @param {Object} data An object containing arbitrary data supplied by the drag source
19143      */
19144     onNodeEnter : function(n, dd, e, data){
19145         
19146     },
19147
19148     /**
19149      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
19150      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
19151      * overridden to provide the proper feedback.
19152      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19153      * {@link #getTargetFromEvent} for this node)
19154      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19155      * @param {Event} e The event
19156      * @param {Object} data An object containing arbitrary data supplied by the drag source
19157      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19158      * underlying {@link Roo.dd.StatusProxy} can be updated
19159      */
19160     onNodeOver : function(n, dd, e, data){
19161         return this.dropAllowed;
19162     },
19163
19164     /**
19165      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
19166      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
19167      * node-specific processing if necessary.
19168      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19169      * {@link #getTargetFromEvent} for this node)
19170      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19171      * @param {Event} e The event
19172      * @param {Object} data An object containing arbitrary data supplied by the drag source
19173      */
19174     onNodeOut : function(n, dd, e, data){
19175         
19176     },
19177
19178     /**
19179      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
19180      * the drop node.  The default implementation returns false, so it should be overridden to provide the
19181      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
19182      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19183      * {@link #getTargetFromEvent} for this node)
19184      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19185      * @param {Event} e The event
19186      * @param {Object} data An object containing arbitrary data supplied by the drag source
19187      * @return {Boolean} True if the drop was valid, else false
19188      */
19189     onNodeDrop : function(n, dd, e, data){
19190         return false;
19191     },
19192
19193     /**
19194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
19195      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
19196      * it should be overridden to provide the proper feedback if necessary.
19197      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19198      * @param {Event} e The event
19199      * @param {Object} data An object containing arbitrary data supplied by the drag source
19200      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19201      * underlying {@link Roo.dd.StatusProxy} can be updated
19202      */
19203     onContainerOver : function(dd, e, data){
19204         return this.dropNotAllowed;
19205     },
19206
19207     /**
19208      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
19209      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
19210      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
19211      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
19212      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19213      * @param {Event} e The event
19214      * @param {Object} data An object containing arbitrary data supplied by the drag source
19215      * @return {Boolean} True if the drop was valid, else false
19216      */
19217     onContainerDrop : function(dd, e, data){
19218         return false;
19219     },
19220
19221     /**
19222      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
19223      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
19224      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
19225      * you should override this method and provide a custom implementation.
19226      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19227      * @param {Event} e The event
19228      * @param {Object} data An object containing arbitrary data supplied by the drag source
19229      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19230      * underlying {@link Roo.dd.StatusProxy} can be updated
19231      */
19232     notifyEnter : function(dd, e, data){
19233         return this.dropNotAllowed;
19234     },
19235
19236     /**
19237      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
19238      * This method will be called on every mouse movement while the drag source is over the drop zone.
19239      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
19240      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
19241      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
19242      * registered node, it will call {@link #onContainerOver}.
19243      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19244      * @param {Event} e The event
19245      * @param {Object} data An object containing arbitrary data supplied by the drag source
19246      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19247      * underlying {@link Roo.dd.StatusProxy} can be updated
19248      */
19249     notifyOver : function(dd, e, data){
19250         var n = this.getTargetFromEvent(e);
19251         if(!n){ // not over valid drop target
19252             if(this.lastOverNode){
19253                 this.onNodeOut(this.lastOverNode, dd, e, data);
19254                 this.lastOverNode = null;
19255             }
19256             return this.onContainerOver(dd, e, data);
19257         }
19258         if(this.lastOverNode != n){
19259             if(this.lastOverNode){
19260                 this.onNodeOut(this.lastOverNode, dd, e, data);
19261             }
19262             this.onNodeEnter(n, dd, e, data);
19263             this.lastOverNode = n;
19264         }
19265         return this.onNodeOver(n, dd, e, data);
19266     },
19267
19268     /**
19269      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
19270      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
19271      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
19272      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
19273      * @param {Event} e The event
19274      * @param {Object} data An object containing arbitrary data supplied by the drag zone
19275      */
19276     notifyOut : function(dd, e, data){
19277         if(this.lastOverNode){
19278             this.onNodeOut(this.lastOverNode, dd, e, data);
19279             this.lastOverNode = null;
19280         }
19281     },
19282
19283     /**
19284      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
19285      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
19286      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
19287      * otherwise it will call {@link #onContainerDrop}.
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      * @return {Boolean} True if the drop was valid, else false
19292      */
19293     notifyDrop : function(dd, e, data){
19294         if(this.lastOverNode){
19295             this.onNodeOut(this.lastOverNode, dd, e, data);
19296             this.lastOverNode = null;
19297         }
19298         var n = this.getTargetFromEvent(e);
19299         return n ?
19300             this.onNodeDrop(n, dd, e, data) :
19301             this.onContainerDrop(dd, e, data);
19302     },
19303
19304     // private
19305     triggerCacheRefresh : function(){
19306         Roo.dd.DDM.refreshCache(this.groups);
19307     }  
19308 });/*
19309  * Based on:
19310  * Ext JS Library 1.1.1
19311  * Copyright(c) 2006-2007, Ext JS, LLC.
19312  *
19313  * Originally Released Under LGPL - original licence link has changed is not relivant.
19314  *
19315  * Fork - LGPL
19316  * <script type="text/javascript">
19317  */
19318
19319
19320 /**
19321  * @class Roo.data.SortTypes
19322  * @singleton
19323  * Defines the default sorting (casting?) comparison functions used when sorting data.
19324  */
19325 Roo.data.SortTypes = {
19326     /**
19327      * Default sort that does nothing
19328      * @param {Mixed} s The value being converted
19329      * @return {Mixed} The comparison value
19330      */
19331     none : function(s){
19332         return s;
19333     },
19334     
19335     /**
19336      * The regular expression used to strip tags
19337      * @type {RegExp}
19338      * @property
19339      */
19340     stripTagsRE : /<\/?[^>]+>/gi,
19341     
19342     /**
19343      * Strips all HTML tags to sort on text only
19344      * @param {Mixed} s The value being converted
19345      * @return {String} The comparison value
19346      */
19347     asText : function(s){
19348         return String(s).replace(this.stripTagsRE, "");
19349     },
19350     
19351     /**
19352      * Strips all HTML tags to sort on text only - Case insensitive
19353      * @param {Mixed} s The value being converted
19354      * @return {String} The comparison value
19355      */
19356     asUCText : function(s){
19357         return String(s).toUpperCase().replace(this.stripTagsRE, "");
19358     },
19359     
19360     /**
19361      * Case insensitive string
19362      * @param {Mixed} s The value being converted
19363      * @return {String} The comparison value
19364      */
19365     asUCString : function(s) {
19366         return String(s).toUpperCase();
19367     },
19368     
19369     /**
19370      * Date sorting
19371      * @param {Mixed} s The value being converted
19372      * @return {Number} The comparison value
19373      */
19374     asDate : function(s) {
19375         if(!s){
19376             return 0;
19377         }
19378         if(s instanceof Date){
19379             return s.getTime();
19380         }
19381         return Date.parse(String(s));
19382     },
19383     
19384     /**
19385      * Float sorting
19386      * @param {Mixed} s The value being converted
19387      * @return {Float} The comparison value
19388      */
19389     asFloat : function(s) {
19390         var val = parseFloat(String(s).replace(/,/g, ""));
19391         if(isNaN(val)) val = 0;
19392         return val;
19393     },
19394     
19395     /**
19396      * Integer sorting
19397      * @param {Mixed} s The value being converted
19398      * @return {Number} The comparison value
19399      */
19400     asInt : function(s) {
19401         var val = parseInt(String(s).replace(/,/g, ""));
19402         if(isNaN(val)) val = 0;
19403         return val;
19404     }
19405 };/*
19406  * Based on:
19407  * Ext JS Library 1.1.1
19408  * Copyright(c) 2006-2007, Ext JS, LLC.
19409  *
19410  * Originally Released Under LGPL - original licence link has changed is not relivant.
19411  *
19412  * Fork - LGPL
19413  * <script type="text/javascript">
19414  */
19415
19416 /**
19417 * @class Roo.data.Record
19418  * Instances of this class encapsulate both record <em>definition</em> information, and record
19419  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
19420  * to access Records cached in an {@link Roo.data.Store} object.<br>
19421  * <p>
19422  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
19423  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
19424  * objects.<br>
19425  * <p>
19426  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
19427  * @constructor
19428  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
19429  * {@link #create}. The parameters are the same.
19430  * @param {Array} data An associative Array of data values keyed by the field name.
19431  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
19432  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
19433  * not specified an integer id is generated.
19434  */
19435 Roo.data.Record = function(data, id){
19436     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
19437     this.data = data;
19438 };
19439
19440 /**
19441  * Generate a constructor for a specific record layout.
19442  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
19443  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
19444  * Each field definition object may contain the following properties: <ul>
19445  * <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,
19446  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
19447  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
19448  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
19449  * is being used, then this is a string containing the javascript expression to reference the data relative to 
19450  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
19451  * to the data item relative to the record element. If the mapping expression is the same as the field name,
19452  * this may be omitted.</p></li>
19453  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
19454  * <ul><li>auto (Default, implies no conversion)</li>
19455  * <li>string</li>
19456  * <li>int</li>
19457  * <li>float</li>
19458  * <li>boolean</li>
19459  * <li>date</li></ul></p></li>
19460  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
19461  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19462  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19463  * by the Reader into an object that will be stored in the Record. It is passed the
19464  * following parameters:<ul>
19465  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19466  * </ul></p></li>
19467  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19468  * </ul>
19469  * <br>usage:<br><pre><code>
19470 var TopicRecord = Roo.data.Record.create(
19471     {name: 'title', mapping: 'topic_title'},
19472     {name: 'author', mapping: 'username'},
19473     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19474     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19475     {name: 'lastPoster', mapping: 'user2'},
19476     {name: 'excerpt', mapping: 'post_text'}
19477 );
19478
19479 var myNewRecord = new TopicRecord({
19480     title: 'Do my job please',
19481     author: 'noobie',
19482     totalPosts: 1,
19483     lastPost: new Date(),
19484     lastPoster: 'Animal',
19485     excerpt: 'No way dude!'
19486 });
19487 myStore.add(myNewRecord);
19488 </code></pre>
19489  * @method create
19490  * @static
19491  */
19492 Roo.data.Record.create = function(o){
19493     var f = function(){
19494         f.superclass.constructor.apply(this, arguments);
19495     };
19496     Roo.extend(f, Roo.data.Record);
19497     var p = f.prototype;
19498     p.fields = new Roo.util.MixedCollection(false, function(field){
19499         return field.name;
19500     });
19501     for(var i = 0, len = o.length; i < len; i++){
19502         p.fields.add(new Roo.data.Field(o[i]));
19503     }
19504     f.getField = function(name){
19505         return p.fields.get(name);  
19506     };
19507     return f;
19508 };
19509
19510 Roo.data.Record.AUTO_ID = 1000;
19511 Roo.data.Record.EDIT = 'edit';
19512 Roo.data.Record.REJECT = 'reject';
19513 Roo.data.Record.COMMIT = 'commit';
19514
19515 Roo.data.Record.prototype = {
19516     /**
19517      * Readonly flag - true if this record has been modified.
19518      * @type Boolean
19519      */
19520     dirty : false,
19521     editing : false,
19522     error: null,
19523     modified: null,
19524
19525     // private
19526     join : function(store){
19527         this.store = store;
19528     },
19529
19530     /**
19531      * Set the named field to the specified value.
19532      * @param {String} name The name of the field to set.
19533      * @param {Object} value The value to set the field to.
19534      */
19535     set : function(name, value){
19536         if(this.data[name] == value){
19537             return;
19538         }
19539         this.dirty = true;
19540         if(!this.modified){
19541             this.modified = {};
19542         }
19543         if(typeof this.modified[name] == 'undefined'){
19544             this.modified[name] = this.data[name];
19545         }
19546         this.data[name] = value;
19547         if(!this.editing && this.store){
19548             this.store.afterEdit(this);
19549         }       
19550     },
19551
19552     /**
19553      * Get the value of the named field.
19554      * @param {String} name The name of the field to get the value of.
19555      * @return {Object} The value of the field.
19556      */
19557     get : function(name){
19558         return this.data[name]; 
19559     },
19560
19561     // private
19562     beginEdit : function(){
19563         this.editing = true;
19564         this.modified = {}; 
19565     },
19566
19567     // private
19568     cancelEdit : function(){
19569         this.editing = false;
19570         delete this.modified;
19571     },
19572
19573     // private
19574     endEdit : function(){
19575         this.editing = false;
19576         if(this.dirty && this.store){
19577             this.store.afterEdit(this);
19578         }
19579     },
19580
19581     /**
19582      * Usually called by the {@link Roo.data.Store} which owns the Record.
19583      * Rejects all changes made to the Record since either creation, or the last commit operation.
19584      * Modified fields are reverted to their original values.
19585      * <p>
19586      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19587      * of reject operations.
19588      */
19589     reject : function(){
19590         var m = this.modified;
19591         for(var n in m){
19592             if(typeof m[n] != "function"){
19593                 this.data[n] = m[n];
19594             }
19595         }
19596         this.dirty = false;
19597         delete this.modified;
19598         this.editing = false;
19599         if(this.store){
19600             this.store.afterReject(this);
19601         }
19602     },
19603
19604     /**
19605      * Usually called by the {@link Roo.data.Store} which owns the Record.
19606      * Commits all changes made to the Record since either creation, or the last commit operation.
19607      * <p>
19608      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19609      * of commit operations.
19610      */
19611     commit : function(){
19612         this.dirty = false;
19613         delete this.modified;
19614         this.editing = false;
19615         if(this.store){
19616             this.store.afterCommit(this);
19617         }
19618     },
19619
19620     // private
19621     hasError : function(){
19622         return this.error != null;
19623     },
19624
19625     // private
19626     clearError : function(){
19627         this.error = null;
19628     },
19629
19630     /**
19631      * Creates a copy of this record.
19632      * @param {String} id (optional) A new record id if you don't want to use this record's id
19633      * @return {Record}
19634      */
19635     copy : function(newId) {
19636         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19637     }
19638 };/*
19639  * Based on:
19640  * Ext JS Library 1.1.1
19641  * Copyright(c) 2006-2007, Ext JS, LLC.
19642  *
19643  * Originally Released Under LGPL - original licence link has changed is not relivant.
19644  *
19645  * Fork - LGPL
19646  * <script type="text/javascript">
19647  */
19648
19649
19650
19651 /**
19652  * @class Roo.data.Store
19653  * @extends Roo.util.Observable
19654  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19655  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19656  * <p>
19657  * 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
19658  * has no knowledge of the format of the data returned by the Proxy.<br>
19659  * <p>
19660  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19661  * instances from the data object. These records are cached and made available through accessor functions.
19662  * @constructor
19663  * Creates a new Store.
19664  * @param {Object} config A config object containing the objects needed for the Store to access data,
19665  * and read the data into Records.
19666  */
19667 Roo.data.Store = function(config){
19668     this.data = new Roo.util.MixedCollection(false);
19669     this.data.getKey = function(o){
19670         return o.id;
19671     };
19672     this.baseParams = {};
19673     // private
19674     this.paramNames = {
19675         "start" : "start",
19676         "limit" : "limit",
19677         "sort" : "sort",
19678         "dir" : "dir",
19679         "multisort" : "_multisort"
19680     };
19681
19682     if(config && config.data){
19683         this.inlineData = config.data;
19684         delete config.data;
19685     }
19686
19687     Roo.apply(this, config);
19688     
19689     if(this.reader){ // reader passed
19690         this.reader = Roo.factory(this.reader, Roo.data);
19691         this.reader.xmodule = this.xmodule || false;
19692         if(!this.recordType){
19693             this.recordType = this.reader.recordType;
19694         }
19695         if(this.reader.onMetaChange){
19696             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19697         }
19698     }
19699
19700     if(this.recordType){
19701         this.fields = this.recordType.prototype.fields;
19702     }
19703     this.modified = [];
19704
19705     this.addEvents({
19706         /**
19707          * @event datachanged
19708          * Fires when the data cache has changed, and a widget which is using this Store
19709          * as a Record cache should refresh its view.
19710          * @param {Store} this
19711          */
19712         datachanged : true,
19713         /**
19714          * @event metachange
19715          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19716          * @param {Store} this
19717          * @param {Object} meta The JSON metadata
19718          */
19719         metachange : true,
19720         /**
19721          * @event add
19722          * Fires when Records have been added to the Store
19723          * @param {Store} this
19724          * @param {Roo.data.Record[]} records The array of Records added
19725          * @param {Number} index The index at which the record(s) were added
19726          */
19727         add : true,
19728         /**
19729          * @event remove
19730          * Fires when a Record has been removed from the Store
19731          * @param {Store} this
19732          * @param {Roo.data.Record} record The Record that was removed
19733          * @param {Number} index The index at which the record was removed
19734          */
19735         remove : true,
19736         /**
19737          * @event update
19738          * Fires when a Record has been updated
19739          * @param {Store} this
19740          * @param {Roo.data.Record} record The Record that was updated
19741          * @param {String} operation The update operation being performed.  Value may be one of:
19742          * <pre><code>
19743  Roo.data.Record.EDIT
19744  Roo.data.Record.REJECT
19745  Roo.data.Record.COMMIT
19746          * </code></pre>
19747          */
19748         update : true,
19749         /**
19750          * @event clear
19751          * Fires when the data cache has been cleared.
19752          * @param {Store} this
19753          */
19754         clear : true,
19755         /**
19756          * @event beforeload
19757          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19758          * the load action will be canceled.
19759          * @param {Store} this
19760          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19761          */
19762         beforeload : true,
19763         /**
19764          * @event beforeloadadd
19765          * Fires after a new set of Records has been loaded.
19766          * @param {Store} this
19767          * @param {Roo.data.Record[]} records The Records that were loaded
19768          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19769          */
19770         beforeloadadd : true,
19771         /**
19772          * @event load
19773          * Fires after a new set of Records has been loaded, before they are added to the store.
19774          * @param {Store} this
19775          * @param {Roo.data.Record[]} records The Records that were loaded
19776          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19777          * @params {Object} return from reader
19778          */
19779         load : true,
19780         /**
19781          * @event loadexception
19782          * Fires if an exception occurs in the Proxy during loading.
19783          * Called with the signature of the Proxy's "loadexception" event.
19784          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19785          * 
19786          * @param {Proxy} 
19787          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19788          * @param {Object} load options 
19789          * @param {Object} jsonData from your request (normally this contains the Exception)
19790          */
19791         loadexception : true
19792     });
19793     
19794     if(this.proxy){
19795         this.proxy = Roo.factory(this.proxy, Roo.data);
19796         this.proxy.xmodule = this.xmodule || false;
19797         this.relayEvents(this.proxy,  ["loadexception"]);
19798     }
19799     this.sortToggle = {};
19800     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19801
19802     Roo.data.Store.superclass.constructor.call(this);
19803
19804     if(this.inlineData){
19805         this.loadData(this.inlineData);
19806         delete this.inlineData;
19807     }
19808 };
19809
19810 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19811      /**
19812     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19813     * without a remote query - used by combo/forms at present.
19814     */
19815     
19816     /**
19817     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19818     */
19819     /**
19820     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19821     */
19822     /**
19823     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19824     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19825     */
19826     /**
19827     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19828     * on any HTTP request
19829     */
19830     /**
19831     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19832     */
19833     /**
19834     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19835     */
19836     multiSort: false,
19837     /**
19838     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19839     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19840     */
19841     remoteSort : false,
19842
19843     /**
19844     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19845      * loaded or when a record is removed. (defaults to false).
19846     */
19847     pruneModifiedRecords : false,
19848
19849     // private
19850     lastOptions : null,
19851
19852     /**
19853      * Add Records to the Store and fires the add event.
19854      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19855      */
19856     add : function(records){
19857         records = [].concat(records);
19858         for(var i = 0, len = records.length; i < len; i++){
19859             records[i].join(this);
19860         }
19861         var index = this.data.length;
19862         this.data.addAll(records);
19863         this.fireEvent("add", this, records, index);
19864     },
19865
19866     /**
19867      * Remove a Record from the Store and fires the remove event.
19868      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19869      */
19870     remove : function(record){
19871         var index = this.data.indexOf(record);
19872         this.data.removeAt(index);
19873         if(this.pruneModifiedRecords){
19874             this.modified.remove(record);
19875         }
19876         this.fireEvent("remove", this, record, index);
19877     },
19878
19879     /**
19880      * Remove all Records from the Store and fires the clear event.
19881      */
19882     removeAll : function(){
19883         this.data.clear();
19884         if(this.pruneModifiedRecords){
19885             this.modified = [];
19886         }
19887         this.fireEvent("clear", this);
19888     },
19889
19890     /**
19891      * Inserts Records to the Store at the given index and fires the add event.
19892      * @param {Number} index The start index at which to insert the passed Records.
19893      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19894      */
19895     insert : function(index, records){
19896         records = [].concat(records);
19897         for(var i = 0, len = records.length; i < len; i++){
19898             this.data.insert(index, records[i]);
19899             records[i].join(this);
19900         }
19901         this.fireEvent("add", this, records, index);
19902     },
19903
19904     /**
19905      * Get the index within the cache of the passed Record.
19906      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19907      * @return {Number} The index of the passed Record. Returns -1 if not found.
19908      */
19909     indexOf : function(record){
19910         return this.data.indexOf(record);
19911     },
19912
19913     /**
19914      * Get the index within the cache of the Record with the passed id.
19915      * @param {String} id The id of the Record to find.
19916      * @return {Number} The index of the Record. Returns -1 if not found.
19917      */
19918     indexOfId : function(id){
19919         return this.data.indexOfKey(id);
19920     },
19921
19922     /**
19923      * Get the Record with the specified id.
19924      * @param {String} id The id of the Record to find.
19925      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19926      */
19927     getById : function(id){
19928         return this.data.key(id);
19929     },
19930
19931     /**
19932      * Get the Record at the specified index.
19933      * @param {Number} index The index of the Record to find.
19934      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19935      */
19936     getAt : function(index){
19937         return this.data.itemAt(index);
19938     },
19939
19940     /**
19941      * Returns a range of Records between specified indices.
19942      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19943      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19944      * @return {Roo.data.Record[]} An array of Records
19945      */
19946     getRange : function(start, end){
19947         return this.data.getRange(start, end);
19948     },
19949
19950     // private
19951     storeOptions : function(o){
19952         o = Roo.apply({}, o);
19953         delete o.callback;
19954         delete o.scope;
19955         this.lastOptions = o;
19956     },
19957
19958     /**
19959      * Loads the Record cache from the configured Proxy using the configured Reader.
19960      * <p>
19961      * If using remote paging, then the first load call must specify the <em>start</em>
19962      * and <em>limit</em> properties in the options.params property to establish the initial
19963      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19964      * <p>
19965      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19966      * and this call will return before the new data has been loaded. Perform any post-processing
19967      * in a callback function, or in a "load" event handler.</strong>
19968      * <p>
19969      * @param {Object} options An object containing properties which control loading options:<ul>
19970      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19971      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19972      * passed the following arguments:<ul>
19973      * <li>r : Roo.data.Record[]</li>
19974      * <li>options: Options object from the load call</li>
19975      * <li>success: Boolean success indicator</li></ul></li>
19976      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19977      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19978      * </ul>
19979      */
19980     load : function(options){
19981         options = options || {};
19982         if(this.fireEvent("beforeload", this, options) !== false){
19983             this.storeOptions(options);
19984             var p = Roo.apply(options.params || {}, this.baseParams);
19985             // if meta was not loaded from remote source.. try requesting it.
19986             if (!this.reader.metaFromRemote) {
19987                 p._requestMeta = 1;
19988             }
19989             if(this.sortInfo && this.remoteSort){
19990                 var pn = this.paramNames;
19991                 p[pn["sort"]] = this.sortInfo.field;
19992                 p[pn["dir"]] = this.sortInfo.direction;
19993             }
19994             if (this.multiSort) {
19995                 var pn = this.paramNames;
19996                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19997             }
19998             
19999             this.proxy.load(p, this.reader, this.loadRecords, this, options);
20000         }
20001     },
20002
20003     /**
20004      * Reloads the Record cache from the configured Proxy using the configured Reader and
20005      * the options from the last load operation performed.
20006      * @param {Object} options (optional) An object containing properties which may override the options
20007      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
20008      * the most recently used options are reused).
20009      */
20010     reload : function(options){
20011         this.load(Roo.applyIf(options||{}, this.lastOptions));
20012     },
20013
20014     // private
20015     // Called as a callback by the Reader during a load operation.
20016     loadRecords : function(o, options, success){
20017         if(!o || success === false){
20018             if(success !== false){
20019                 this.fireEvent("load", this, [], options, o);
20020             }
20021             if(options.callback){
20022                 options.callback.call(options.scope || this, [], options, false);
20023             }
20024             return;
20025         }
20026         // if data returned failure - throw an exception.
20027         if (o.success === false) {
20028             // show a message if no listener is registered.
20029             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
20030                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
20031             }
20032             // loadmask wil be hooked into this..
20033             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
20034             return;
20035         }
20036         var r = o.records, t = o.totalRecords || r.length;
20037         
20038         this.fireEvent("beforeloadadd", this, r, options, o);
20039         
20040         if(!options || options.add !== true){
20041             if(this.pruneModifiedRecords){
20042                 this.modified = [];
20043             }
20044             for(var i = 0, len = r.length; i < len; i++){
20045                 r[i].join(this);
20046             }
20047             if(this.snapshot){
20048                 this.data = this.snapshot;
20049                 delete this.snapshot;
20050             }
20051             this.data.clear();
20052             this.data.addAll(r);
20053             this.totalLength = t;
20054             this.applySort();
20055             this.fireEvent("datachanged", this);
20056         }else{
20057             this.totalLength = Math.max(t, this.data.length+r.length);
20058             this.add(r);
20059         }
20060         this.fireEvent("load", this, r, options, o);
20061         if(options.callback){
20062             options.callback.call(options.scope || this, r, options, true);
20063         }
20064     },
20065
20066
20067     /**
20068      * Loads data from a passed data block. A Reader which understands the format of the data
20069      * must have been configured in the constructor.
20070      * @param {Object} data The data block from which to read the Records.  The format of the data expected
20071      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
20072      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
20073      */
20074     loadData : function(o, append){
20075         var r = this.reader.readRecords(o);
20076         this.loadRecords(r, {add: append}, true);
20077     },
20078
20079     /**
20080      * Gets the number of cached records.
20081      * <p>
20082      * <em>If using paging, this may not be the total size of the dataset. If the data object
20083      * used by the Reader contains the dataset size, then the getTotalCount() function returns
20084      * the data set size</em>
20085      */
20086     getCount : function(){
20087         return this.data.length || 0;
20088     },
20089
20090     /**
20091      * Gets the total number of records in the dataset as returned by the server.
20092      * <p>
20093      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
20094      * the dataset size</em>
20095      */
20096     getTotalCount : function(){
20097         return this.totalLength || 0;
20098     },
20099
20100     /**
20101      * Returns the sort state of the Store as an object with two properties:
20102      * <pre><code>
20103  field {String} The name of the field by which the Records are sorted
20104  direction {String} The sort order, "ASC" or "DESC"
20105      * </code></pre>
20106      */
20107     getSortState : function(){
20108         return this.sortInfo;
20109     },
20110
20111     // private
20112     applySort : function(){
20113         if(this.sortInfo && !this.remoteSort){
20114             var s = this.sortInfo, f = s.field;
20115             var st = this.fields.get(f).sortType;
20116             var fn = function(r1, r2){
20117                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
20118                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
20119             };
20120             this.data.sort(s.direction, fn);
20121             if(this.snapshot && this.snapshot != this.data){
20122                 this.snapshot.sort(s.direction, fn);
20123             }
20124         }
20125     },
20126
20127     /**
20128      * Sets the default sort column and order to be used by the next load operation.
20129      * @param {String} fieldName The name of the field to sort by.
20130      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
20131      */
20132     setDefaultSort : function(field, dir){
20133         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
20134     },
20135
20136     /**
20137      * Sort the Records.
20138      * If remote sorting is used, the sort is performed on the server, and the cache is
20139      * reloaded. If local sorting is used, the cache is sorted internally.
20140      * @param {String} fieldName The name of the field to sort by.
20141      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
20142      */
20143     sort : function(fieldName, dir){
20144         var f = this.fields.get(fieldName);
20145         if(!dir){
20146             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
20147             
20148             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
20149                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
20150             }else{
20151                 dir = f.sortDir;
20152             }
20153         }
20154         this.sortToggle[f.name] = dir;
20155         this.sortInfo = {field: f.name, direction: dir};
20156         if(!this.remoteSort){
20157             this.applySort();
20158             this.fireEvent("datachanged", this);
20159         }else{
20160             this.load(this.lastOptions);
20161         }
20162     },
20163
20164     /**
20165      * Calls the specified function for each of the Records in the cache.
20166      * @param {Function} fn The function to call. The Record is passed as the first parameter.
20167      * Returning <em>false</em> aborts and exits the iteration.
20168      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
20169      */
20170     each : function(fn, scope){
20171         this.data.each(fn, scope);
20172     },
20173
20174     /**
20175      * Gets all records modified since the last commit.  Modified records are persisted across load operations
20176      * (e.g., during paging).
20177      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
20178      */
20179     getModifiedRecords : function(){
20180         return this.modified;
20181     },
20182
20183     // private
20184     createFilterFn : function(property, value, anyMatch){
20185         if(!value.exec){ // not a regex
20186             value = String(value);
20187             if(value.length == 0){
20188                 return false;
20189             }
20190             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
20191         }
20192         return function(r){
20193             return value.test(r.data[property]);
20194         };
20195     },
20196
20197     /**
20198      * Sums the value of <i>property</i> for each record between start and end and returns the result.
20199      * @param {String} property A field on your records
20200      * @param {Number} start The record index to start at (defaults to 0)
20201      * @param {Number} end The last record index to include (defaults to length - 1)
20202      * @return {Number} The sum
20203      */
20204     sum : function(property, start, end){
20205         var rs = this.data.items, v = 0;
20206         start = start || 0;
20207         end = (end || end === 0) ? end : rs.length-1;
20208
20209         for(var i = start; i <= end; i++){
20210             v += (rs[i].data[property] || 0);
20211         }
20212         return v;
20213     },
20214
20215     /**
20216      * Filter the records by a specified property.
20217      * @param {String} field A field on your records
20218      * @param {String/RegExp} value Either a string that the field
20219      * should start with or a RegExp to test against the field
20220      * @param {Boolean} anyMatch True to match any part not just the beginning
20221      */
20222     filter : function(property, value, anyMatch){
20223         var fn = this.createFilterFn(property, value, anyMatch);
20224         return fn ? this.filterBy(fn) : this.clearFilter();
20225     },
20226
20227     /**
20228      * Filter by a function. The specified function will be called with each
20229      * record in this data source. If the function returns true the record is included,
20230      * otherwise it is filtered.
20231      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
20232      * @param {Object} scope (optional) The scope of the function (defaults to this)
20233      */
20234     filterBy : function(fn, scope){
20235         this.snapshot = this.snapshot || this.data;
20236         this.data = this.queryBy(fn, scope||this);
20237         this.fireEvent("datachanged", this);
20238     },
20239
20240     /**
20241      * Query the records by a specified property.
20242      * @param {String} field A field on your records
20243      * @param {String/RegExp} value Either a string that the field
20244      * should start with or a RegExp to test against the field
20245      * @param {Boolean} anyMatch True to match any part not just the beginning
20246      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
20247      */
20248     query : function(property, value, anyMatch){
20249         var fn = this.createFilterFn(property, value, anyMatch);
20250         return fn ? this.queryBy(fn) : this.data.clone();
20251     },
20252
20253     /**
20254      * Query by a function. The specified function will be called with each
20255      * record in this data source. If the function returns true the record is included
20256      * in the results.
20257      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
20258      * @param {Object} scope (optional) The scope of the function (defaults to this)
20259       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
20260      **/
20261     queryBy : function(fn, scope){
20262         var data = this.snapshot || this.data;
20263         return data.filterBy(fn, scope||this);
20264     },
20265
20266     /**
20267      * Collects unique values for a particular dataIndex from this store.
20268      * @param {String} dataIndex The property to collect
20269      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
20270      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
20271      * @return {Array} An array of the unique values
20272      **/
20273     collect : function(dataIndex, allowNull, bypassFilter){
20274         var d = (bypassFilter === true && this.snapshot) ?
20275                 this.snapshot.items : this.data.items;
20276         var v, sv, r = [], l = {};
20277         for(var i = 0, len = d.length; i < len; i++){
20278             v = d[i].data[dataIndex];
20279             sv = String(v);
20280             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
20281                 l[sv] = true;
20282                 r[r.length] = v;
20283             }
20284         }
20285         return r;
20286     },
20287
20288     /**
20289      * Revert to a view of the Record cache with no filtering applied.
20290      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
20291      */
20292     clearFilter : function(suppressEvent){
20293         if(this.snapshot && this.snapshot != this.data){
20294             this.data = this.snapshot;
20295             delete this.snapshot;
20296             if(suppressEvent !== true){
20297                 this.fireEvent("datachanged", this);
20298             }
20299         }
20300     },
20301
20302     // private
20303     afterEdit : function(record){
20304         if(this.modified.indexOf(record) == -1){
20305             this.modified.push(record);
20306         }
20307         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
20308     },
20309     
20310     // private
20311     afterReject : function(record){
20312         this.modified.remove(record);
20313         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
20314     },
20315
20316     // private
20317     afterCommit : function(record){
20318         this.modified.remove(record);
20319         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
20320     },
20321
20322     /**
20323      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
20324      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
20325      */
20326     commitChanges : function(){
20327         var m = this.modified.slice(0);
20328         this.modified = [];
20329         for(var i = 0, len = m.length; i < len; i++){
20330             m[i].commit();
20331         }
20332     },
20333
20334     /**
20335      * Cancel outstanding changes on all changed records.
20336      */
20337     rejectChanges : function(){
20338         var m = this.modified.slice(0);
20339         this.modified = [];
20340         for(var i = 0, len = m.length; i < len; i++){
20341             m[i].reject();
20342         }
20343     },
20344
20345     onMetaChange : function(meta, rtype, o){
20346         this.recordType = rtype;
20347         this.fields = rtype.prototype.fields;
20348         delete this.snapshot;
20349         this.sortInfo = meta.sortInfo || this.sortInfo;
20350         this.modified = [];
20351         this.fireEvent('metachange', this, this.reader.meta);
20352     }
20353 });/*
20354  * Based on:
20355  * Ext JS Library 1.1.1
20356  * Copyright(c) 2006-2007, Ext JS, LLC.
20357  *
20358  * Originally Released Under LGPL - original licence link has changed is not relivant.
20359  *
20360  * Fork - LGPL
20361  * <script type="text/javascript">
20362  */
20363
20364 /**
20365  * @class Roo.data.SimpleStore
20366  * @extends Roo.data.Store
20367  * Small helper class to make creating Stores from Array data easier.
20368  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
20369  * @cfg {Array} fields An array of field definition objects, or field name strings.
20370  * @cfg {Array} data The multi-dimensional array of data
20371  * @constructor
20372  * @param {Object} config
20373  */
20374 Roo.data.SimpleStore = function(config){
20375     Roo.data.SimpleStore.superclass.constructor.call(this, {
20376         isLocal : true,
20377         reader: new Roo.data.ArrayReader({
20378                 id: config.id
20379             },
20380             Roo.data.Record.create(config.fields)
20381         ),
20382         proxy : new Roo.data.MemoryProxy(config.data)
20383     });
20384     this.load();
20385 };
20386 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
20387  * Based on:
20388  * Ext JS Library 1.1.1
20389  * Copyright(c) 2006-2007, Ext JS, LLC.
20390  *
20391  * Originally Released Under LGPL - original licence link has changed is not relivant.
20392  *
20393  * Fork - LGPL
20394  * <script type="text/javascript">
20395  */
20396
20397 /**
20398 /**
20399  * @extends Roo.data.Store
20400  * @class Roo.data.JsonStore
20401  * Small helper class to make creating Stores for JSON data easier. <br/>
20402 <pre><code>
20403 var store = new Roo.data.JsonStore({
20404     url: 'get-images.php',
20405     root: 'images',
20406     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
20407 });
20408 </code></pre>
20409  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
20410  * JsonReader and HttpProxy (unless inline data is provided).</b>
20411  * @cfg {Array} fields An array of field definition objects, or field name strings.
20412  * @constructor
20413  * @param {Object} config
20414  */
20415 Roo.data.JsonStore = function(c){
20416     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
20417         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
20418         reader: new Roo.data.JsonReader(c, c.fields)
20419     }));
20420 };
20421 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
20422  * Based on:
20423  * Ext JS Library 1.1.1
20424  * Copyright(c) 2006-2007, Ext JS, LLC.
20425  *
20426  * Originally Released Under LGPL - original licence link has changed is not relivant.
20427  *
20428  * Fork - LGPL
20429  * <script type="text/javascript">
20430  */
20431
20432  
20433 Roo.data.Field = function(config){
20434     if(typeof config == "string"){
20435         config = {name: config};
20436     }
20437     Roo.apply(this, config);
20438     
20439     if(!this.type){
20440         this.type = "auto";
20441     }
20442     
20443     var st = Roo.data.SortTypes;
20444     // named sortTypes are supported, here we look them up
20445     if(typeof this.sortType == "string"){
20446         this.sortType = st[this.sortType];
20447     }
20448     
20449     // set default sortType for strings and dates
20450     if(!this.sortType){
20451         switch(this.type){
20452             case "string":
20453                 this.sortType = st.asUCString;
20454                 break;
20455             case "date":
20456                 this.sortType = st.asDate;
20457                 break;
20458             default:
20459                 this.sortType = st.none;
20460         }
20461     }
20462
20463     // define once
20464     var stripRe = /[\$,%]/g;
20465
20466     // prebuilt conversion function for this field, instead of
20467     // switching every time we're reading a value
20468     if(!this.convert){
20469         var cv, dateFormat = this.dateFormat;
20470         switch(this.type){
20471             case "":
20472             case "auto":
20473             case undefined:
20474                 cv = function(v){ return v; };
20475                 break;
20476             case "string":
20477                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20478                 break;
20479             case "int":
20480                 cv = function(v){
20481                     return v !== undefined && v !== null && v !== '' ?
20482                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20483                     };
20484                 break;
20485             case "float":
20486                 cv = function(v){
20487                     return v !== undefined && v !== null && v !== '' ?
20488                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20489                     };
20490                 break;
20491             case "bool":
20492             case "boolean":
20493                 cv = function(v){ return v === true || v === "true" || v == 1; };
20494                 break;
20495             case "date":
20496                 cv = function(v){
20497                     if(!v){
20498                         return '';
20499                     }
20500                     if(v instanceof Date){
20501                         return v;
20502                     }
20503                     if(dateFormat){
20504                         if(dateFormat == "timestamp"){
20505                             return new Date(v*1000);
20506                         }
20507                         return Date.parseDate(v, dateFormat);
20508                     }
20509                     var parsed = Date.parse(v);
20510                     return parsed ? new Date(parsed) : null;
20511                 };
20512              break;
20513             
20514         }
20515         this.convert = cv;
20516     }
20517 };
20518
20519 Roo.data.Field.prototype = {
20520     dateFormat: null,
20521     defaultValue: "",
20522     mapping: null,
20523     sortType : null,
20524     sortDir : "ASC"
20525 };/*
20526  * Based on:
20527  * Ext JS Library 1.1.1
20528  * Copyright(c) 2006-2007, Ext JS, LLC.
20529  *
20530  * Originally Released Under LGPL - original licence link has changed is not relivant.
20531  *
20532  * Fork - LGPL
20533  * <script type="text/javascript">
20534  */
20535  
20536 // Base class for reading structured data from a data source.  This class is intended to be
20537 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20538
20539 /**
20540  * @class Roo.data.DataReader
20541  * Base class for reading structured data from a data source.  This class is intended to be
20542  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20543  */
20544
20545 Roo.data.DataReader = function(meta, recordType){
20546     
20547     this.meta = meta;
20548     
20549     this.recordType = recordType instanceof Array ? 
20550         Roo.data.Record.create(recordType) : recordType;
20551 };
20552
20553 Roo.data.DataReader.prototype = {
20554      /**
20555      * Create an empty record
20556      * @param {Object} data (optional) - overlay some values
20557      * @return {Roo.data.Record} record created.
20558      */
20559     newRow :  function(d) {
20560         var da =  {};
20561         this.recordType.prototype.fields.each(function(c) {
20562             switch( c.type) {
20563                 case 'int' : da[c.name] = 0; break;
20564                 case 'date' : da[c.name] = new Date(); break;
20565                 case 'float' : da[c.name] = 0.0; break;
20566                 case 'boolean' : da[c.name] = false; break;
20567                 default : da[c.name] = ""; break;
20568             }
20569             
20570         });
20571         return new this.recordType(Roo.apply(da, d));
20572     }
20573     
20574 };/*
20575  * Based on:
20576  * Ext JS Library 1.1.1
20577  * Copyright(c) 2006-2007, Ext JS, LLC.
20578  *
20579  * Originally Released Under LGPL - original licence link has changed is not relivant.
20580  *
20581  * Fork - LGPL
20582  * <script type="text/javascript">
20583  */
20584
20585 /**
20586  * @class Roo.data.DataProxy
20587  * @extends Roo.data.Observable
20588  * This class is an abstract base class for implementations which provide retrieval of
20589  * unformatted data objects.<br>
20590  * <p>
20591  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20592  * (of the appropriate type which knows how to parse the data object) to provide a block of
20593  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20594  * <p>
20595  * Custom implementations must implement the load method as described in
20596  * {@link Roo.data.HttpProxy#load}.
20597  */
20598 Roo.data.DataProxy = function(){
20599     this.addEvents({
20600         /**
20601          * @event beforeload
20602          * Fires before a network request is made to retrieve a data object.
20603          * @param {Object} This DataProxy object.
20604          * @param {Object} params The params parameter to the load function.
20605          */
20606         beforeload : true,
20607         /**
20608          * @event load
20609          * Fires before the load method's callback is called.
20610          * @param {Object} This DataProxy object.
20611          * @param {Object} o The data object.
20612          * @param {Object} arg The callback argument object passed to the load function.
20613          */
20614         load : true,
20615         /**
20616          * @event loadexception
20617          * Fires if an Exception occurs during data retrieval.
20618          * @param {Object} This DataProxy object.
20619          * @param {Object} o The data object.
20620          * @param {Object} arg The callback argument object passed to the load function.
20621          * @param {Object} e The Exception.
20622          */
20623         loadexception : true
20624     });
20625     Roo.data.DataProxy.superclass.constructor.call(this);
20626 };
20627
20628 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20629
20630     /**
20631      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20632      */
20633 /*
20634  * Based on:
20635  * Ext JS Library 1.1.1
20636  * Copyright(c) 2006-2007, Ext JS, LLC.
20637  *
20638  * Originally Released Under LGPL - original licence link has changed is not relivant.
20639  *
20640  * Fork - LGPL
20641  * <script type="text/javascript">
20642  */
20643 /**
20644  * @class Roo.data.MemoryProxy
20645  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20646  * to the Reader when its load method is called.
20647  * @constructor
20648  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20649  */
20650 Roo.data.MemoryProxy = function(data){
20651     if (data.data) {
20652         data = data.data;
20653     }
20654     Roo.data.MemoryProxy.superclass.constructor.call(this);
20655     this.data = data;
20656 };
20657
20658 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20659     /**
20660      * Load data from the requested source (in this case an in-memory
20661      * data object passed to the constructor), read the data object into
20662      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20663      * process that block using the passed callback.
20664      * @param {Object} params This parameter is not used by the MemoryProxy class.
20665      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20666      * object into a block of Roo.data.Records.
20667      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20668      * The function must be passed <ul>
20669      * <li>The Record block object</li>
20670      * <li>The "arg" argument from the load function</li>
20671      * <li>A boolean success indicator</li>
20672      * </ul>
20673      * @param {Object} scope The scope in which to call the callback
20674      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20675      */
20676     load : function(params, reader, callback, scope, arg){
20677         params = params || {};
20678         var result;
20679         try {
20680             result = reader.readRecords(this.data);
20681         }catch(e){
20682             this.fireEvent("loadexception", this, arg, null, e);
20683             callback.call(scope, null, arg, false);
20684             return;
20685         }
20686         callback.call(scope, result, arg, true);
20687     },
20688     
20689     // private
20690     update : function(params, records){
20691         
20692     }
20693 });/*
20694  * Based on:
20695  * Ext JS Library 1.1.1
20696  * Copyright(c) 2006-2007, Ext JS, LLC.
20697  *
20698  * Originally Released Under LGPL - original licence link has changed is not relivant.
20699  *
20700  * Fork - LGPL
20701  * <script type="text/javascript">
20702  */
20703 /**
20704  * @class Roo.data.HttpProxy
20705  * @extends Roo.data.DataProxy
20706  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20707  * configured to reference a certain URL.<br><br>
20708  * <p>
20709  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20710  * from which the running page was served.<br><br>
20711  * <p>
20712  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20713  * <p>
20714  * Be aware that to enable the browser to parse an XML document, the server must set
20715  * the Content-Type header in the HTTP response to "text/xml".
20716  * @constructor
20717  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20718  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20719  * will be used to make the request.
20720  */
20721 Roo.data.HttpProxy = function(conn){
20722     Roo.data.HttpProxy.superclass.constructor.call(this);
20723     // is conn a conn config or a real conn?
20724     this.conn = conn;
20725     this.useAjax = !conn || !conn.events;
20726   
20727 };
20728
20729 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20730     // thse are take from connection...
20731     
20732     /**
20733      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20734      */
20735     /**
20736      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20737      * extra parameters to each request made by this object. (defaults to undefined)
20738      */
20739     /**
20740      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20741      *  to each request made by this object. (defaults to undefined)
20742      */
20743     /**
20744      * @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)
20745      */
20746     /**
20747      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20748      */
20749      /**
20750      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20751      * @type Boolean
20752      */
20753   
20754
20755     /**
20756      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20757      * @type Boolean
20758      */
20759     /**
20760      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20761      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20762      * a finer-grained basis than the DataProxy events.
20763      */
20764     getConnection : function(){
20765         return this.useAjax ? Roo.Ajax : this.conn;
20766     },
20767
20768     /**
20769      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20770      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20771      * process that block using the passed callback.
20772      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20773      * for the request to the remote server.
20774      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20775      * object into a block of Roo.data.Records.
20776      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20777      * The function must be passed <ul>
20778      * <li>The Record block object</li>
20779      * <li>The "arg" argument from the load function</li>
20780      * <li>A boolean success indicator</li>
20781      * </ul>
20782      * @param {Object} scope The scope in which to call the callback
20783      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20784      */
20785     load : function(params, reader, callback, scope, arg){
20786         if(this.fireEvent("beforeload", this, params) !== false){
20787             var  o = {
20788                 params : params || {},
20789                 request: {
20790                     callback : callback,
20791                     scope : scope,
20792                     arg : arg
20793                 },
20794                 reader: reader,
20795                 callback : this.loadResponse,
20796                 scope: this
20797             };
20798             if(this.useAjax){
20799                 Roo.applyIf(o, this.conn);
20800                 if(this.activeRequest){
20801                     Roo.Ajax.abort(this.activeRequest);
20802                 }
20803                 this.activeRequest = Roo.Ajax.request(o);
20804             }else{
20805                 this.conn.request(o);
20806             }
20807         }else{
20808             callback.call(scope||this, null, arg, false);
20809         }
20810     },
20811
20812     // private
20813     loadResponse : function(o, success, response){
20814         delete this.activeRequest;
20815         if(!success){
20816             this.fireEvent("loadexception", this, o, response);
20817             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20818             return;
20819         }
20820         var result;
20821         try {
20822             result = o.reader.read(response);
20823         }catch(e){
20824             this.fireEvent("loadexception", this, o, response, e);
20825             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20826             return;
20827         }
20828         
20829         this.fireEvent("load", this, o, o.request.arg);
20830         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20831     },
20832
20833     // private
20834     update : function(dataSet){
20835
20836     },
20837
20838     // private
20839     updateResponse : function(dataSet){
20840
20841     }
20842 });/*
20843  * Based on:
20844  * Ext JS Library 1.1.1
20845  * Copyright(c) 2006-2007, Ext JS, LLC.
20846  *
20847  * Originally Released Under LGPL - original licence link has changed is not relivant.
20848  *
20849  * Fork - LGPL
20850  * <script type="text/javascript">
20851  */
20852
20853 /**
20854  * @class Roo.data.ScriptTagProxy
20855  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20856  * other than the originating domain of the running page.<br><br>
20857  * <p>
20858  * <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
20859  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20860  * <p>
20861  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20862  * source code that is used as the source inside a &lt;script> tag.<br><br>
20863  * <p>
20864  * In order for the browser to process the returned data, the server must wrap the data object
20865  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20866  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20867  * depending on whether the callback name was passed:
20868  * <p>
20869  * <pre><code>
20870 boolean scriptTag = false;
20871 String cb = request.getParameter("callback");
20872 if (cb != null) {
20873     scriptTag = true;
20874     response.setContentType("text/javascript");
20875 } else {
20876     response.setContentType("application/x-json");
20877 }
20878 Writer out = response.getWriter();
20879 if (scriptTag) {
20880     out.write(cb + "(");
20881 }
20882 out.print(dataBlock.toJsonString());
20883 if (scriptTag) {
20884     out.write(");");
20885 }
20886 </pre></code>
20887  *
20888  * @constructor
20889  * @param {Object} config A configuration object.
20890  */
20891 Roo.data.ScriptTagProxy = function(config){
20892     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20893     Roo.apply(this, config);
20894     this.head = document.getElementsByTagName("head")[0];
20895 };
20896
20897 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20898
20899 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20900     /**
20901      * @cfg {String} url The URL from which to request the data object.
20902      */
20903     /**
20904      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20905      */
20906     timeout : 30000,
20907     /**
20908      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20909      * the server the name of the callback function set up by the load call to process the returned data object.
20910      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20911      * javascript output which calls this named function passing the data object as its only parameter.
20912      */
20913     callbackParam : "callback",
20914     /**
20915      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20916      * name to the request.
20917      */
20918     nocache : true,
20919
20920     /**
20921      * Load data from the configured URL, read the data object into
20922      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20923      * process that block using the passed callback.
20924      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20925      * for the request to the remote server.
20926      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20927      * object into a block of Roo.data.Records.
20928      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20929      * The function must be passed <ul>
20930      * <li>The Record block object</li>
20931      * <li>The "arg" argument from the load function</li>
20932      * <li>A boolean success indicator</li>
20933      * </ul>
20934      * @param {Object} scope The scope in which to call the callback
20935      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20936      */
20937     load : function(params, reader, callback, scope, arg){
20938         if(this.fireEvent("beforeload", this, params) !== false){
20939
20940             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20941
20942             var url = this.url;
20943             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20944             if(this.nocache){
20945                 url += "&_dc=" + (new Date().getTime());
20946             }
20947             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20948             var trans = {
20949                 id : transId,
20950                 cb : "stcCallback"+transId,
20951                 scriptId : "stcScript"+transId,
20952                 params : params,
20953                 arg : arg,
20954                 url : url,
20955                 callback : callback,
20956                 scope : scope,
20957                 reader : reader
20958             };
20959             var conn = this;
20960
20961             window[trans.cb] = function(o){
20962                 conn.handleResponse(o, trans);
20963             };
20964
20965             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20966
20967             if(this.autoAbort !== false){
20968                 this.abort();
20969             }
20970
20971             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20972
20973             var script = document.createElement("script");
20974             script.setAttribute("src", url);
20975             script.setAttribute("type", "text/javascript");
20976             script.setAttribute("id", trans.scriptId);
20977             this.head.appendChild(script);
20978
20979             this.trans = trans;
20980         }else{
20981             callback.call(scope||this, null, arg, false);
20982         }
20983     },
20984
20985     // private
20986     isLoading : function(){
20987         return this.trans ? true : false;
20988     },
20989
20990     /**
20991      * Abort the current server request.
20992      */
20993     abort : function(){
20994         if(this.isLoading()){
20995             this.destroyTrans(this.trans);
20996         }
20997     },
20998
20999     // private
21000     destroyTrans : function(trans, isLoaded){
21001         this.head.removeChild(document.getElementById(trans.scriptId));
21002         clearTimeout(trans.timeoutId);
21003         if(isLoaded){
21004             window[trans.cb] = undefined;
21005             try{
21006                 delete window[trans.cb];
21007             }catch(e){}
21008         }else{
21009             // if hasn't been loaded, wait for load to remove it to prevent script error
21010             window[trans.cb] = function(){
21011                 window[trans.cb] = undefined;
21012                 try{
21013                     delete window[trans.cb];
21014                 }catch(e){}
21015             };
21016         }
21017     },
21018
21019     // private
21020     handleResponse : function(o, trans){
21021         this.trans = false;
21022         this.destroyTrans(trans, true);
21023         var result;
21024         try {
21025             result = trans.reader.readRecords(o);
21026         }catch(e){
21027             this.fireEvent("loadexception", this, o, trans.arg, e);
21028             trans.callback.call(trans.scope||window, null, trans.arg, false);
21029             return;
21030         }
21031         this.fireEvent("load", this, o, trans.arg);
21032         trans.callback.call(trans.scope||window, result, trans.arg, true);
21033     },
21034
21035     // private
21036     handleFailure : function(trans){
21037         this.trans = false;
21038         this.destroyTrans(trans, false);
21039         this.fireEvent("loadexception", this, null, trans.arg);
21040         trans.callback.call(trans.scope||window, null, trans.arg, false);
21041     }
21042 });/*
21043  * Based on:
21044  * Ext JS Library 1.1.1
21045  * Copyright(c) 2006-2007, Ext JS, LLC.
21046  *
21047  * Originally Released Under LGPL - original licence link has changed is not relivant.
21048  *
21049  * Fork - LGPL
21050  * <script type="text/javascript">
21051  */
21052
21053 /**
21054  * @class Roo.data.JsonReader
21055  * @extends Roo.data.DataReader
21056  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
21057  * based on mappings in a provided Roo.data.Record constructor.
21058  * 
21059  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
21060  * in the reply previously. 
21061  * 
21062  * <p>
21063  * Example code:
21064  * <pre><code>
21065 var RecordDef = Roo.data.Record.create([
21066     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
21067     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
21068 ]);
21069 var myReader = new Roo.data.JsonReader({
21070     totalProperty: "results",    // The property which contains the total dataset size (optional)
21071     root: "rows",                // The property which contains an Array of row objects
21072     id: "id"                     // The property within each row object that provides an ID for the record (optional)
21073 }, RecordDef);
21074 </code></pre>
21075  * <p>
21076  * This would consume a JSON file like this:
21077  * <pre><code>
21078 { 'results': 2, 'rows': [
21079     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
21080     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
21081 }
21082 </code></pre>
21083  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
21084  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
21085  * paged from the remote server.
21086  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
21087  * @cfg {String} root name of the property which contains the Array of row objects.
21088  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
21089  * @constructor
21090  * Create a new JsonReader
21091  * @param {Object} meta Metadata configuration options
21092  * @param {Object} recordType Either an Array of field definition objects,
21093  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
21094  */
21095 Roo.data.JsonReader = function(meta, recordType){
21096     
21097     meta = meta || {};
21098     // set some defaults:
21099     Roo.applyIf(meta, {
21100         totalProperty: 'total',
21101         successProperty : 'success',
21102         root : 'data',
21103         id : 'id'
21104     });
21105     
21106     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
21107 };
21108 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
21109     
21110     /**
21111      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
21112      * Used by Store query builder to append _requestMeta to params.
21113      * 
21114      */
21115     metaFromRemote : false,
21116     /**
21117      * This method is only used by a DataProxy which has retrieved data from a remote server.
21118      * @param {Object} response The XHR object which contains the JSON data in its responseText.
21119      * @return {Object} data A data block which is used by an Roo.data.Store object as
21120      * a cache of Roo.data.Records.
21121      */
21122     read : function(response){
21123         var json = response.responseText;
21124        
21125         var o = /* eval:var:o */ eval("("+json+")");
21126         if(!o) {
21127             throw {message: "JsonReader.read: Json object not found"};
21128         }
21129         
21130         if(o.metaData){
21131             
21132             delete this.ef;
21133             this.metaFromRemote = true;
21134             this.meta = o.metaData;
21135             this.recordType = Roo.data.Record.create(o.metaData.fields);
21136             this.onMetaChange(this.meta, this.recordType, o);
21137         }
21138         return this.readRecords(o);
21139     },
21140
21141     // private function a store will implement
21142     onMetaChange : function(meta, recordType, o){
21143
21144     },
21145
21146     /**
21147          * @ignore
21148          */
21149     simpleAccess: function(obj, subsc) {
21150         return obj[subsc];
21151     },
21152
21153         /**
21154          * @ignore
21155          */
21156     getJsonAccessor: function(){
21157         var re = /[\[\.]/;
21158         return function(expr) {
21159             try {
21160                 return(re.test(expr))
21161                     ? new Function("obj", "return obj." + expr)
21162                     : function(obj){
21163                         return obj[expr];
21164                     };
21165             } catch(e){}
21166             return Roo.emptyFn;
21167         };
21168     }(),
21169
21170     /**
21171      * Create a data block containing Roo.data.Records from an XML document.
21172      * @param {Object} o An object which contains an Array of row objects in the property specified
21173      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
21174      * which contains the total size of the dataset.
21175      * @return {Object} data A data block which is used by an Roo.data.Store object as
21176      * a cache of Roo.data.Records.
21177      */
21178     readRecords : function(o){
21179         /**
21180          * After any data loads, the raw JSON data is available for further custom processing.
21181          * @type Object
21182          */
21183         this.o = o;
21184         var s = this.meta, Record = this.recordType,
21185             f = Record.prototype.fields, fi = f.items, fl = f.length;
21186
21187 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
21188         if (!this.ef) {
21189             if(s.totalProperty) {
21190                     this.getTotal = this.getJsonAccessor(s.totalProperty);
21191                 }
21192                 if(s.successProperty) {
21193                     this.getSuccess = this.getJsonAccessor(s.successProperty);
21194                 }
21195                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
21196                 if (s.id) {
21197                         var g = this.getJsonAccessor(s.id);
21198                         this.getId = function(rec) {
21199                                 var r = g(rec);
21200                                 return (r === undefined || r === "") ? null : r;
21201                         };
21202                 } else {
21203                         this.getId = function(){return null;};
21204                 }
21205             this.ef = [];
21206             for(var jj = 0; jj < fl; jj++){
21207                 f = fi[jj];
21208                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
21209                 this.ef[jj] = this.getJsonAccessor(map);
21210             }
21211         }
21212
21213         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
21214         if(s.totalProperty){
21215             var vt = parseInt(this.getTotal(o), 10);
21216             if(!isNaN(vt)){
21217                 totalRecords = vt;
21218             }
21219         }
21220         if(s.successProperty){
21221             var vs = this.getSuccess(o);
21222             if(vs === false || vs === 'false'){
21223                 success = false;
21224             }
21225         }
21226         var records = [];
21227             for(var i = 0; i < c; i++){
21228                     var n = root[i];
21229                 var values = {};
21230                 var id = this.getId(n);
21231                 for(var j = 0; j < fl; j++){
21232                     f = fi[j];
21233                 var v = this.ef[j](n);
21234                 if (!f.convert) {
21235                     Roo.log('missing convert for ' + f.name);
21236                     Roo.log(f);
21237                     continue;
21238                 }
21239                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
21240                 }
21241                 var record = new Record(values, id);
21242                 record.json = n;
21243                 records[i] = record;
21244             }
21245             return {
21246             raw : o,
21247                 success : success,
21248                 records : records,
21249                 totalRecords : totalRecords
21250             };
21251     }
21252 });/*
21253  * Based on:
21254  * Ext JS Library 1.1.1
21255  * Copyright(c) 2006-2007, Ext JS, LLC.
21256  *
21257  * Originally Released Under LGPL - original licence link has changed is not relivant.
21258  *
21259  * Fork - LGPL
21260  * <script type="text/javascript">
21261  */
21262
21263 /**
21264  * @class Roo.data.XmlReader
21265  * @extends Roo.data.DataReader
21266  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
21267  * based on mappings in a provided Roo.data.Record constructor.<br><br>
21268  * <p>
21269  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
21270  * header in the HTTP response must be set to "text/xml".</em>
21271  * <p>
21272  * Example code:
21273  * <pre><code>
21274 var RecordDef = Roo.data.Record.create([
21275    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
21276    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
21277 ]);
21278 var myReader = new Roo.data.XmlReader({
21279    totalRecords: "results", // The element which contains the total dataset size (optional)
21280    record: "row",           // The repeated element which contains row information
21281    id: "id"                 // The element within the row that provides an ID for the record (optional)
21282 }, RecordDef);
21283 </code></pre>
21284  * <p>
21285  * This would consume an XML file like this:
21286  * <pre><code>
21287 &lt;?xml?>
21288 &lt;dataset>
21289  &lt;results>2&lt;/results>
21290  &lt;row>
21291    &lt;id>1&lt;/id>
21292    &lt;name>Bill&lt;/name>
21293    &lt;occupation>Gardener&lt;/occupation>
21294  &lt;/row>
21295  &lt;row>
21296    &lt;id>2&lt;/id>
21297    &lt;name>Ben&lt;/name>
21298    &lt;occupation>Horticulturalist&lt;/occupation>
21299  &lt;/row>
21300 &lt;/dataset>
21301 </code></pre>
21302  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
21303  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
21304  * paged from the remote server.
21305  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
21306  * @cfg {String} success The DomQuery path to the success attribute used by forms.
21307  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
21308  * a record identifier value.
21309  * @constructor
21310  * Create a new XmlReader
21311  * @param {Object} meta Metadata configuration options
21312  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
21313  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
21314  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
21315  */
21316 Roo.data.XmlReader = function(meta, recordType){
21317     meta = meta || {};
21318     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
21319 };
21320 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
21321     /**
21322      * This method is only used by a DataProxy which has retrieved data from a remote server.
21323          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
21324          * to contain a method called 'responseXML' that returns an XML document object.
21325      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
21326      * a cache of Roo.data.Records.
21327      */
21328     read : function(response){
21329         var doc = response.responseXML;
21330         if(!doc) {
21331             throw {message: "XmlReader.read: XML Document not available"};
21332         }
21333         return this.readRecords(doc);
21334     },
21335
21336     /**
21337      * Create a data block containing Roo.data.Records from an XML document.
21338          * @param {Object} doc A parsed XML document.
21339      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
21340      * a cache of Roo.data.Records.
21341      */
21342     readRecords : function(doc){
21343         /**
21344          * After any data loads/reads, the raw XML Document is available for further custom processing.
21345          * @type XMLDocument
21346          */
21347         this.xmlData = doc;
21348         var root = doc.documentElement || doc;
21349         var q = Roo.DomQuery;
21350         var recordType = this.recordType, fields = recordType.prototype.fields;
21351         var sid = this.meta.id;
21352         var totalRecords = 0, success = true;
21353         if(this.meta.totalRecords){
21354             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
21355         }
21356         
21357         if(this.meta.success){
21358             var sv = q.selectValue(this.meta.success, root, true);
21359             success = sv !== false && sv !== 'false';
21360         }
21361         var records = [];
21362         var ns = q.select(this.meta.record, root);
21363         for(var i = 0, len = ns.length; i < len; i++) {
21364                 var n = ns[i];
21365                 var values = {};
21366                 var id = sid ? q.selectValue(sid, n) : undefined;
21367                 for(var j = 0, jlen = fields.length; j < jlen; j++){
21368                     var f = fields.items[j];
21369                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
21370                     v = f.convert(v);
21371                     values[f.name] = v;
21372                 }
21373                 var record = new recordType(values, id);
21374                 record.node = n;
21375                 records[records.length] = record;
21376             }
21377
21378             return {
21379                 success : success,
21380                 records : records,
21381                 totalRecords : totalRecords || records.length
21382             };
21383     }
21384 });/*
21385  * Based on:
21386  * Ext JS Library 1.1.1
21387  * Copyright(c) 2006-2007, Ext JS, LLC.
21388  *
21389  * Originally Released Under LGPL - original licence link has changed is not relivant.
21390  *
21391  * Fork - LGPL
21392  * <script type="text/javascript">
21393  */
21394
21395 /**
21396  * @class Roo.data.ArrayReader
21397  * @extends Roo.data.DataReader
21398  * Data reader class to create an Array of Roo.data.Record objects from an Array.
21399  * Each element of that Array represents a row of data fields. The
21400  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
21401  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
21402  * <p>
21403  * Example code:.
21404  * <pre><code>
21405 var RecordDef = Roo.data.Record.create([
21406     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
21407     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
21408 ]);
21409 var myReader = new Roo.data.ArrayReader({
21410     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
21411 }, RecordDef);
21412 </code></pre>
21413  * <p>
21414  * This would consume an Array like this:
21415  * <pre><code>
21416 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
21417   </code></pre>
21418  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
21419  * @constructor
21420  * Create a new JsonReader
21421  * @param {Object} meta Metadata configuration options.
21422  * @param {Object} recordType Either an Array of field definition objects
21423  * as specified to {@link Roo.data.Record#create},
21424  * or an {@link Roo.data.Record} object
21425  * created using {@link Roo.data.Record#create}.
21426  */
21427 Roo.data.ArrayReader = function(meta, recordType){
21428     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
21429 };
21430
21431 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
21432     /**
21433      * Create a data block containing Roo.data.Records from an XML document.
21434      * @param {Object} o An Array of row objects which represents the dataset.
21435      * @return {Object} data A data block which is used by an Roo.data.Store object as
21436      * a cache of Roo.data.Records.
21437      */
21438     readRecords : function(o){
21439         var sid = this.meta ? this.meta.id : null;
21440         var recordType = this.recordType, fields = recordType.prototype.fields;
21441         var records = [];
21442         var root = o;
21443             for(var i = 0; i < root.length; i++){
21444                     var n = root[i];
21445                 var values = {};
21446                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
21447                 for(var j = 0, jlen = fields.length; j < jlen; j++){
21448                 var f = fields.items[j];
21449                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
21450                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
21451                 v = f.convert(v);
21452                 values[f.name] = v;
21453             }
21454                 var record = new recordType(values, id);
21455                 record.json = n;
21456                 records[records.length] = record;
21457             }
21458             return {
21459                 records : records,
21460                 totalRecords : records.length
21461             };
21462     }
21463 });/*
21464  * Based on:
21465  * Ext JS Library 1.1.1
21466  * Copyright(c) 2006-2007, Ext JS, LLC.
21467  *
21468  * Originally Released Under LGPL - original licence link has changed is not relivant.
21469  *
21470  * Fork - LGPL
21471  * <script type="text/javascript">
21472  */
21473
21474
21475 /**
21476  * @class Roo.data.Tree
21477  * @extends Roo.util.Observable
21478  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21479  * in the tree have most standard DOM functionality.
21480  * @constructor
21481  * @param {Node} root (optional) The root node
21482  */
21483 Roo.data.Tree = function(root){
21484    this.nodeHash = {};
21485    /**
21486     * The root node for this tree
21487     * @type Node
21488     */
21489    this.root = null;
21490    if(root){
21491        this.setRootNode(root);
21492    }
21493    this.addEvents({
21494        /**
21495         * @event append
21496         * Fires when a new child node is appended to a node in this tree.
21497         * @param {Tree} tree The owner tree
21498         * @param {Node} parent The parent node
21499         * @param {Node} node The newly appended node
21500         * @param {Number} index The index of the newly appended node
21501         */
21502        "append" : true,
21503        /**
21504         * @event remove
21505         * Fires when a child node is removed from a node in this tree.
21506         * @param {Tree} tree The owner tree
21507         * @param {Node} parent The parent node
21508         * @param {Node} node The child node removed
21509         */
21510        "remove" : true,
21511        /**
21512         * @event move
21513         * Fires when a node is moved to a new location in the tree
21514         * @param {Tree} tree The owner tree
21515         * @param {Node} node The node moved
21516         * @param {Node} oldParent The old parent of this node
21517         * @param {Node} newParent The new parent of this node
21518         * @param {Number} index The index it was moved to
21519         */
21520        "move" : true,
21521        /**
21522         * @event insert
21523         * Fires when a new child node is inserted in a node in this tree.
21524         * @param {Tree} tree The owner tree
21525         * @param {Node} parent The parent node
21526         * @param {Node} node The child node inserted
21527         * @param {Node} refNode The child node the node was inserted before
21528         */
21529        "insert" : true,
21530        /**
21531         * @event beforeappend
21532         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21533         * @param {Tree} tree The owner tree
21534         * @param {Node} parent The parent node
21535         * @param {Node} node The child node to be appended
21536         */
21537        "beforeappend" : true,
21538        /**
21539         * @event beforeremove
21540         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21541         * @param {Tree} tree The owner tree
21542         * @param {Node} parent The parent node
21543         * @param {Node} node The child node to be removed
21544         */
21545        "beforeremove" : true,
21546        /**
21547         * @event beforemove
21548         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21549         * @param {Tree} tree The owner tree
21550         * @param {Node} node The node being moved
21551         * @param {Node} oldParent The parent of the node
21552         * @param {Node} newParent The new parent the node is moving to
21553         * @param {Number} index The index it is being moved to
21554         */
21555        "beforemove" : true,
21556        /**
21557         * @event beforeinsert
21558         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21559         * @param {Tree} tree The owner tree
21560         * @param {Node} parent The parent node
21561         * @param {Node} node The child node to be inserted
21562         * @param {Node} refNode The child node the node is being inserted before
21563         */
21564        "beforeinsert" : true
21565    });
21566
21567     Roo.data.Tree.superclass.constructor.call(this);
21568 };
21569
21570 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21571     pathSeparator: "/",
21572
21573     proxyNodeEvent : function(){
21574         return this.fireEvent.apply(this, arguments);
21575     },
21576
21577     /**
21578      * Returns the root node for this tree.
21579      * @return {Node}
21580      */
21581     getRootNode : function(){
21582         return this.root;
21583     },
21584
21585     /**
21586      * Sets the root node for this tree.
21587      * @param {Node} node
21588      * @return {Node}
21589      */
21590     setRootNode : function(node){
21591         this.root = node;
21592         node.ownerTree = this;
21593         node.isRoot = true;
21594         this.registerNode(node);
21595         return node;
21596     },
21597
21598     /**
21599      * Gets a node in this tree by its id.
21600      * @param {String} id
21601      * @return {Node}
21602      */
21603     getNodeById : function(id){
21604         return this.nodeHash[id];
21605     },
21606
21607     registerNode : function(node){
21608         this.nodeHash[node.id] = node;
21609     },
21610
21611     unregisterNode : function(node){
21612         delete this.nodeHash[node.id];
21613     },
21614
21615     toString : function(){
21616         return "[Tree"+(this.id?" "+this.id:"")+"]";
21617     }
21618 });
21619
21620 /**
21621  * @class Roo.data.Node
21622  * @extends Roo.util.Observable
21623  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21624  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21625  * @constructor
21626  * @param {Object} attributes The attributes/config for the node
21627  */
21628 Roo.data.Node = function(attributes){
21629     /**
21630      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21631      * @type {Object}
21632      */
21633     this.attributes = attributes || {};
21634     this.leaf = this.attributes.leaf;
21635     /**
21636      * The node id. @type String
21637      */
21638     this.id = this.attributes.id;
21639     if(!this.id){
21640         this.id = Roo.id(null, "ynode-");
21641         this.attributes.id = this.id;
21642     }
21643      
21644     
21645     /**
21646      * All child nodes of this node. @type Array
21647      */
21648     this.childNodes = [];
21649     if(!this.childNodes.indexOf){ // indexOf is a must
21650         this.childNodes.indexOf = function(o){
21651             for(var i = 0, len = this.length; i < len; i++){
21652                 if(this[i] == o) {
21653                     return i;
21654                 }
21655             }
21656             return -1;
21657         };
21658     }
21659     /**
21660      * The parent node for this node. @type Node
21661      */
21662     this.parentNode = null;
21663     /**
21664      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21665      */
21666     this.firstChild = null;
21667     /**
21668      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21669      */
21670     this.lastChild = null;
21671     /**
21672      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21673      */
21674     this.previousSibling = null;
21675     /**
21676      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21677      */
21678     this.nextSibling = null;
21679
21680     this.addEvents({
21681        /**
21682         * @event append
21683         * Fires when a new child node is appended
21684         * @param {Tree} tree The owner tree
21685         * @param {Node} this This node
21686         * @param {Node} node The newly appended node
21687         * @param {Number} index The index of the newly appended node
21688         */
21689        "append" : true,
21690        /**
21691         * @event remove
21692         * Fires when a child node is removed
21693         * @param {Tree} tree The owner tree
21694         * @param {Node} this This node
21695         * @param {Node} node The removed node
21696         */
21697        "remove" : true,
21698        /**
21699         * @event move
21700         * Fires when this node is moved to a new location in the tree
21701         * @param {Tree} tree The owner tree
21702         * @param {Node} this This node
21703         * @param {Node} oldParent The old parent of this node
21704         * @param {Node} newParent The new parent of this node
21705         * @param {Number} index The index it was moved to
21706         */
21707        "move" : true,
21708        /**
21709         * @event insert
21710         * Fires when a new child node is inserted.
21711         * @param {Tree} tree The owner tree
21712         * @param {Node} this This node
21713         * @param {Node} node The child node inserted
21714         * @param {Node} refNode The child node the node was inserted before
21715         */
21716        "insert" : true,
21717        /**
21718         * @event beforeappend
21719         * Fires before a new child is appended, return false to cancel the append.
21720         * @param {Tree} tree The owner tree
21721         * @param {Node} this This node
21722         * @param {Node} node The child node to be appended
21723         */
21724        "beforeappend" : true,
21725        /**
21726         * @event beforeremove
21727         * Fires before a child is removed, return false to cancel the remove.
21728         * @param {Tree} tree The owner tree
21729         * @param {Node} this This node
21730         * @param {Node} node The child node to be removed
21731         */
21732        "beforeremove" : true,
21733        /**
21734         * @event beforemove
21735         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21736         * @param {Tree} tree The owner tree
21737         * @param {Node} this This node
21738         * @param {Node} oldParent The parent of this node
21739         * @param {Node} newParent The new parent this node is moving to
21740         * @param {Number} index The index it is being moved to
21741         */
21742        "beforemove" : true,
21743        /**
21744         * @event beforeinsert
21745         * Fires before a new child is inserted, return false to cancel the insert.
21746         * @param {Tree} tree The owner tree
21747         * @param {Node} this This node
21748         * @param {Node} node The child node to be inserted
21749         * @param {Node} refNode The child node the node is being inserted before
21750         */
21751        "beforeinsert" : true
21752    });
21753     this.listeners = this.attributes.listeners;
21754     Roo.data.Node.superclass.constructor.call(this);
21755 };
21756
21757 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21758     fireEvent : function(evtName){
21759         // first do standard event for this node
21760         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21761             return false;
21762         }
21763         // then bubble it up to the tree if the event wasn't cancelled
21764         var ot = this.getOwnerTree();
21765         if(ot){
21766             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21767                 return false;
21768             }
21769         }
21770         return true;
21771     },
21772
21773     /**
21774      * Returns true if this node is a leaf
21775      * @return {Boolean}
21776      */
21777     isLeaf : function(){
21778         return this.leaf === true;
21779     },
21780
21781     // private
21782     setFirstChild : function(node){
21783         this.firstChild = node;
21784     },
21785
21786     //private
21787     setLastChild : function(node){
21788         this.lastChild = node;
21789     },
21790
21791
21792     /**
21793      * Returns true if this node is the last child of its parent
21794      * @return {Boolean}
21795      */
21796     isLast : function(){
21797        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21798     },
21799
21800     /**
21801      * Returns true if this node is the first child of its parent
21802      * @return {Boolean}
21803      */
21804     isFirst : function(){
21805        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21806     },
21807
21808     hasChildNodes : function(){
21809         return !this.isLeaf() && this.childNodes.length > 0;
21810     },
21811
21812     /**
21813      * Insert node(s) as the last child node of this node.
21814      * @param {Node/Array} node The node or Array of nodes to append
21815      * @return {Node} The appended node if single append, or null if an array was passed
21816      */
21817     appendChild : function(node){
21818         var multi = false;
21819         if(node instanceof Array){
21820             multi = node;
21821         }else if(arguments.length > 1){
21822             multi = arguments;
21823         }
21824         // if passed an array or multiple args do them one by one
21825         if(multi){
21826             for(var i = 0, len = multi.length; i < len; i++) {
21827                 this.appendChild(multi[i]);
21828             }
21829         }else{
21830             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21831                 return false;
21832             }
21833             var index = this.childNodes.length;
21834             var oldParent = node.parentNode;
21835             // it's a move, make sure we move it cleanly
21836             if(oldParent){
21837                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21838                     return false;
21839                 }
21840                 oldParent.removeChild(node);
21841             }
21842             index = this.childNodes.length;
21843             if(index == 0){
21844                 this.setFirstChild(node);
21845             }
21846             this.childNodes.push(node);
21847             node.parentNode = this;
21848             var ps = this.childNodes[index-1];
21849             if(ps){
21850                 node.previousSibling = ps;
21851                 ps.nextSibling = node;
21852             }else{
21853                 node.previousSibling = null;
21854             }
21855             node.nextSibling = null;
21856             this.setLastChild(node);
21857             node.setOwnerTree(this.getOwnerTree());
21858             this.fireEvent("append", this.ownerTree, this, node, index);
21859             if(oldParent){
21860                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21861             }
21862             return node;
21863         }
21864     },
21865
21866     /**
21867      * Removes a child node from this node.
21868      * @param {Node} node The node to remove
21869      * @return {Node} The removed node
21870      */
21871     removeChild : function(node){
21872         var index = this.childNodes.indexOf(node);
21873         if(index == -1){
21874             return false;
21875         }
21876         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21877             return false;
21878         }
21879
21880         // remove it from childNodes collection
21881         this.childNodes.splice(index, 1);
21882
21883         // update siblings
21884         if(node.previousSibling){
21885             node.previousSibling.nextSibling = node.nextSibling;
21886         }
21887         if(node.nextSibling){
21888             node.nextSibling.previousSibling = node.previousSibling;
21889         }
21890
21891         // update child refs
21892         if(this.firstChild == node){
21893             this.setFirstChild(node.nextSibling);
21894         }
21895         if(this.lastChild == node){
21896             this.setLastChild(node.previousSibling);
21897         }
21898
21899         node.setOwnerTree(null);
21900         // clear any references from the node
21901         node.parentNode = null;
21902         node.previousSibling = null;
21903         node.nextSibling = null;
21904         this.fireEvent("remove", this.ownerTree, this, node);
21905         return node;
21906     },
21907
21908     /**
21909      * Inserts the first node before the second node in this nodes childNodes collection.
21910      * @param {Node} node The node to insert
21911      * @param {Node} refNode The node to insert before (if null the node is appended)
21912      * @return {Node} The inserted node
21913      */
21914     insertBefore : function(node, refNode){
21915         if(!refNode){ // like standard Dom, refNode can be null for append
21916             return this.appendChild(node);
21917         }
21918         // nothing to do
21919         if(node == refNode){
21920             return false;
21921         }
21922
21923         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21924             return false;
21925         }
21926         var index = this.childNodes.indexOf(refNode);
21927         var oldParent = node.parentNode;
21928         var refIndex = index;
21929
21930         // when moving internally, indexes will change after remove
21931         if(oldParent == this && this.childNodes.indexOf(node) < index){
21932             refIndex--;
21933         }
21934
21935         // it's a move, make sure we move it cleanly
21936         if(oldParent){
21937             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21938                 return false;
21939             }
21940             oldParent.removeChild(node);
21941         }
21942         if(refIndex == 0){
21943             this.setFirstChild(node);
21944         }
21945         this.childNodes.splice(refIndex, 0, node);
21946         node.parentNode = this;
21947         var ps = this.childNodes[refIndex-1];
21948         if(ps){
21949             node.previousSibling = ps;
21950             ps.nextSibling = node;
21951         }else{
21952             node.previousSibling = null;
21953         }
21954         node.nextSibling = refNode;
21955         refNode.previousSibling = node;
21956         node.setOwnerTree(this.getOwnerTree());
21957         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21958         if(oldParent){
21959             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21960         }
21961         return node;
21962     },
21963
21964     /**
21965      * Returns the child node at the specified index.
21966      * @param {Number} index
21967      * @return {Node}
21968      */
21969     item : function(index){
21970         return this.childNodes[index];
21971     },
21972
21973     /**
21974      * Replaces one child node in this node with another.
21975      * @param {Node} newChild The replacement node
21976      * @param {Node} oldChild The node to replace
21977      * @return {Node} The replaced node
21978      */
21979     replaceChild : function(newChild, oldChild){
21980         this.insertBefore(newChild, oldChild);
21981         this.removeChild(oldChild);
21982         return oldChild;
21983     },
21984
21985     /**
21986      * Returns the index of a child node
21987      * @param {Node} node
21988      * @return {Number} The index of the node or -1 if it was not found
21989      */
21990     indexOf : function(child){
21991         return this.childNodes.indexOf(child);
21992     },
21993
21994     /**
21995      * Returns the tree this node is in.
21996      * @return {Tree}
21997      */
21998     getOwnerTree : function(){
21999         // if it doesn't have one, look for one
22000         if(!this.ownerTree){
22001             var p = this;
22002             while(p){
22003                 if(p.ownerTree){
22004                     this.ownerTree = p.ownerTree;
22005                     break;
22006                 }
22007                 p = p.parentNode;
22008             }
22009         }
22010         return this.ownerTree;
22011     },
22012
22013     /**
22014      * Returns depth of this node (the root node has a depth of 0)
22015      * @return {Number}
22016      */
22017     getDepth : function(){
22018         var depth = 0;
22019         var p = this;
22020         while(p.parentNode){
22021             ++depth;
22022             p = p.parentNode;
22023         }
22024         return depth;
22025     },
22026
22027     // private
22028     setOwnerTree : function(tree){
22029         // if it's move, we need to update everyone
22030         if(tree != this.ownerTree){
22031             if(this.ownerTree){
22032                 this.ownerTree.unregisterNode(this);
22033             }
22034             this.ownerTree = tree;
22035             var cs = this.childNodes;
22036             for(var i = 0, len = cs.length; i < len; i++) {
22037                 cs[i].setOwnerTree(tree);
22038             }
22039             if(tree){
22040                 tree.registerNode(this);
22041             }
22042         }
22043     },
22044
22045     /**
22046      * Returns the path for this node. The path can be used to expand or select this node programmatically.
22047      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
22048      * @return {String} The path
22049      */
22050     getPath : function(attr){
22051         attr = attr || "id";
22052         var p = this.parentNode;
22053         var b = [this.attributes[attr]];
22054         while(p){
22055             b.unshift(p.attributes[attr]);
22056             p = p.parentNode;
22057         }
22058         var sep = this.getOwnerTree().pathSeparator;
22059         return sep + b.join(sep);
22060     },
22061
22062     /**
22063      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
22064      * function call will be the scope provided or the current node. The arguments to the function
22065      * will be the args provided or the current node. If the function returns false at any point,
22066      * the bubble is stopped.
22067      * @param {Function} fn The function to call
22068      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22069      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22070      */
22071     bubble : function(fn, scope, args){
22072         var p = this;
22073         while(p){
22074             if(fn.call(scope || p, args || p) === false){
22075                 break;
22076             }
22077             p = p.parentNode;
22078         }
22079     },
22080
22081     /**
22082      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
22083      * function call will be the scope provided or the current node. The arguments to the function
22084      * will be the args provided or the current node. If the function returns false at any point,
22085      * the cascade is stopped on that branch.
22086      * @param {Function} fn The function to call
22087      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22088      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22089      */
22090     cascade : function(fn, scope, args){
22091         if(fn.call(scope || this, args || this) !== false){
22092             var cs = this.childNodes;
22093             for(var i = 0, len = cs.length; i < len; i++) {
22094                 cs[i].cascade(fn, scope, args);
22095             }
22096         }
22097     },
22098
22099     /**
22100      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
22101      * function call will be the scope provided or the current node. The arguments to the function
22102      * will be the args provided or the current node. If the function returns false at any point,
22103      * the iteration stops.
22104      * @param {Function} fn The function to call
22105      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22106      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22107      */
22108     eachChild : function(fn, scope, args){
22109         var cs = this.childNodes;
22110         for(var i = 0, len = cs.length; i < len; i++) {
22111                 if(fn.call(scope || this, args || cs[i]) === false){
22112                     break;
22113                 }
22114         }
22115     },
22116
22117     /**
22118      * Finds the first child that has the attribute with the specified value.
22119      * @param {String} attribute The attribute name
22120      * @param {Mixed} value The value to search for
22121      * @return {Node} The found child or null if none was found
22122      */
22123     findChild : function(attribute, value){
22124         var cs = this.childNodes;
22125         for(var i = 0, len = cs.length; i < len; i++) {
22126                 if(cs[i].attributes[attribute] == value){
22127                     return cs[i];
22128                 }
22129         }
22130         return null;
22131     },
22132
22133     /**
22134      * Finds the first child by a custom function. The child matches if the function passed
22135      * returns true.
22136      * @param {Function} fn
22137      * @param {Object} scope (optional)
22138      * @return {Node} The found child or null if none was found
22139      */
22140     findChildBy : function(fn, scope){
22141         var cs = this.childNodes;
22142         for(var i = 0, len = cs.length; i < len; i++) {
22143                 if(fn.call(scope||cs[i], cs[i]) === true){
22144                     return cs[i];
22145                 }
22146         }
22147         return null;
22148     },
22149
22150     /**
22151      * Sorts this nodes children using the supplied sort function
22152      * @param {Function} fn
22153      * @param {Object} scope (optional)
22154      */
22155     sort : function(fn, scope){
22156         var cs = this.childNodes;
22157         var len = cs.length;
22158         if(len > 0){
22159             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
22160             cs.sort(sortFn);
22161             for(var i = 0; i < len; i++){
22162                 var n = cs[i];
22163                 n.previousSibling = cs[i-1];
22164                 n.nextSibling = cs[i+1];
22165                 if(i == 0){
22166                     this.setFirstChild(n);
22167                 }
22168                 if(i == len-1){
22169                     this.setLastChild(n);
22170                 }
22171             }
22172         }
22173     },
22174
22175     /**
22176      * Returns true if this node is an ancestor (at any point) of the passed node.
22177      * @param {Node} node
22178      * @return {Boolean}
22179      */
22180     contains : function(node){
22181         return node.isAncestor(this);
22182     },
22183
22184     /**
22185      * Returns true if the passed node is an ancestor (at any point) of this node.
22186      * @param {Node} node
22187      * @return {Boolean}
22188      */
22189     isAncestor : function(node){
22190         var p = this.parentNode;
22191         while(p){
22192             if(p == node){
22193                 return true;
22194             }
22195             p = p.parentNode;
22196         }
22197         return false;
22198     },
22199
22200     toString : function(){
22201         return "[Node"+(this.id?" "+this.id:"")+"]";
22202     }
22203 });/*
22204  * Based on:
22205  * Ext JS Library 1.1.1
22206  * Copyright(c) 2006-2007, Ext JS, LLC.
22207  *
22208  * Originally Released Under LGPL - original licence link has changed is not relivant.
22209  *
22210  * Fork - LGPL
22211  * <script type="text/javascript">
22212  */
22213  
22214
22215 /**
22216  * @class Roo.ComponentMgr
22217  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
22218  * @singleton
22219  */
22220 Roo.ComponentMgr = function(){
22221     var all = new Roo.util.MixedCollection();
22222
22223     return {
22224         /**
22225          * Registers a component.
22226          * @param {Roo.Component} c The component
22227          */
22228         register : function(c){
22229             all.add(c);
22230         },
22231
22232         /**
22233          * Unregisters a component.
22234          * @param {Roo.Component} c The component
22235          */
22236         unregister : function(c){
22237             all.remove(c);
22238         },
22239
22240         /**
22241          * Returns a component by id
22242          * @param {String} id The component id
22243          */
22244         get : function(id){
22245             return all.get(id);
22246         },
22247
22248         /**
22249          * Registers a function that will be called when a specified component is added to ComponentMgr
22250          * @param {String} id The component id
22251          * @param {Funtction} fn The callback function
22252          * @param {Object} scope The scope of the callback
22253          */
22254         onAvailable : function(id, fn, scope){
22255             all.on("add", function(index, o){
22256                 if(o.id == id){
22257                     fn.call(scope || o, o);
22258                     all.un("add", fn, scope);
22259                 }
22260             });
22261         }
22262     };
22263 }();/*
22264  * Based on:
22265  * Ext JS Library 1.1.1
22266  * Copyright(c) 2006-2007, Ext JS, LLC.
22267  *
22268  * Originally Released Under LGPL - original licence link has changed is not relivant.
22269  *
22270  * Fork - LGPL
22271  * <script type="text/javascript">
22272  */
22273  
22274 /**
22275  * @class Roo.Component
22276  * @extends Roo.util.Observable
22277  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
22278  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
22279  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
22280  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
22281  * All visual components (widgets) that require rendering into a layout should subclass Component.
22282  * @constructor
22283  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
22284  * 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
22285  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
22286  */
22287 Roo.Component = function(config){
22288     config = config || {};
22289     if(config.tagName || config.dom || typeof config == "string"){ // element object
22290         config = {el: config, id: config.id || config};
22291     }
22292     this.initialConfig = config;
22293
22294     Roo.apply(this, config);
22295     this.addEvents({
22296         /**
22297          * @event disable
22298          * Fires after the component is disabled.
22299              * @param {Roo.Component} this
22300              */
22301         disable : true,
22302         /**
22303          * @event enable
22304          * Fires after the component is enabled.
22305              * @param {Roo.Component} this
22306              */
22307         enable : true,
22308         /**
22309          * @event beforeshow
22310          * Fires before the component is shown.  Return false to stop the show.
22311              * @param {Roo.Component} this
22312              */
22313         beforeshow : true,
22314         /**
22315          * @event show
22316          * Fires after the component is shown.
22317              * @param {Roo.Component} this
22318              */
22319         show : true,
22320         /**
22321          * @event beforehide
22322          * Fires before the component is hidden. Return false to stop the hide.
22323              * @param {Roo.Component} this
22324              */
22325         beforehide : true,
22326         /**
22327          * @event hide
22328          * Fires after the component is hidden.
22329              * @param {Roo.Component} this
22330              */
22331         hide : true,
22332         /**
22333          * @event beforerender
22334          * Fires before the component is rendered. Return false to stop the render.
22335              * @param {Roo.Component} this
22336              */
22337         beforerender : true,
22338         /**
22339          * @event render
22340          * Fires after the component is rendered.
22341              * @param {Roo.Component} this
22342              */
22343         render : true,
22344         /**
22345          * @event beforedestroy
22346          * Fires before the component is destroyed. Return false to stop the destroy.
22347              * @param {Roo.Component} this
22348              */
22349         beforedestroy : true,
22350         /**
22351          * @event destroy
22352          * Fires after the component is destroyed.
22353              * @param {Roo.Component} this
22354              */
22355         destroy : true
22356     });
22357     if(!this.id){
22358         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
22359     }
22360     Roo.ComponentMgr.register(this);
22361     Roo.Component.superclass.constructor.call(this);
22362     this.initComponent();
22363     if(this.renderTo){ // not supported by all components yet. use at your own risk!
22364         this.render(this.renderTo);
22365         delete this.renderTo;
22366     }
22367 };
22368
22369 /** @private */
22370 Roo.Component.AUTO_ID = 1000;
22371
22372 Roo.extend(Roo.Component, Roo.util.Observable, {
22373     /**
22374      * @scope Roo.Component.prototype
22375      * @type {Boolean}
22376      * true if this component is hidden. Read-only.
22377      */
22378     hidden : false,
22379     /**
22380      * @type {Boolean}
22381      * true if this component is disabled. Read-only.
22382      */
22383     disabled : false,
22384     /**
22385      * @type {Boolean}
22386      * true if this component has been rendered. Read-only.
22387      */
22388     rendered : false,
22389     
22390     /** @cfg {String} disableClass
22391      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
22392      */
22393     disabledClass : "x-item-disabled",
22394         /** @cfg {Boolean} allowDomMove
22395          * Whether the component can move the Dom node when rendering (defaults to true).
22396          */
22397     allowDomMove : true,
22398     /** @cfg {String} hideMode
22399      * How this component should hidden. Supported values are
22400      * "visibility" (css visibility), "offsets" (negative offset position) and
22401      * "display" (css display) - defaults to "display".
22402      */
22403     hideMode: 'display',
22404
22405     /** @private */
22406     ctype : "Roo.Component",
22407
22408     /**
22409      * @cfg {String} actionMode 
22410      * which property holds the element that used for  hide() / show() / disable() / enable()
22411      * default is 'el' 
22412      */
22413     actionMode : "el",
22414
22415     /** @private */
22416     getActionEl : function(){
22417         return this[this.actionMode];
22418     },
22419
22420     initComponent : Roo.emptyFn,
22421     /**
22422      * If this is a lazy rendering component, render it to its container element.
22423      * @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.
22424      */
22425     render : function(container, position){
22426         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
22427             if(!container && this.el){
22428                 this.el = Roo.get(this.el);
22429                 container = this.el.dom.parentNode;
22430                 this.allowDomMove = false;
22431             }
22432             this.container = Roo.get(container);
22433             this.rendered = true;
22434             if(position !== undefined){
22435                 if(typeof position == 'number'){
22436                     position = this.container.dom.childNodes[position];
22437                 }else{
22438                     position = Roo.getDom(position);
22439                 }
22440             }
22441             this.onRender(this.container, position || null);
22442             if(this.cls){
22443                 this.el.addClass(this.cls);
22444                 delete this.cls;
22445             }
22446             if(this.style){
22447                 this.el.applyStyles(this.style);
22448                 delete this.style;
22449             }
22450             this.fireEvent("render", this);
22451             this.afterRender(this.container);
22452             if(this.hidden){
22453                 this.hide();
22454             }
22455             if(this.disabled){
22456                 this.disable();
22457             }
22458         }
22459         return this;
22460     },
22461
22462     /** @private */
22463     // default function is not really useful
22464     onRender : function(ct, position){
22465         if(this.el){
22466             this.el = Roo.get(this.el);
22467             if(this.allowDomMove !== false){
22468                 ct.dom.insertBefore(this.el.dom, position);
22469             }
22470         }
22471     },
22472
22473     /** @private */
22474     getAutoCreate : function(){
22475         var cfg = typeof this.autoCreate == "object" ?
22476                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
22477         if(this.id && !cfg.id){
22478             cfg.id = this.id;
22479         }
22480         return cfg;
22481     },
22482
22483     /** @private */
22484     afterRender : Roo.emptyFn,
22485
22486     /**
22487      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22488      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22489      */
22490     destroy : function(){
22491         if(this.fireEvent("beforedestroy", this) !== false){
22492             this.purgeListeners();
22493             this.beforeDestroy();
22494             if(this.rendered){
22495                 this.el.removeAllListeners();
22496                 this.el.remove();
22497                 if(this.actionMode == "container"){
22498                     this.container.remove();
22499                 }
22500             }
22501             this.onDestroy();
22502             Roo.ComponentMgr.unregister(this);
22503             this.fireEvent("destroy", this);
22504         }
22505     },
22506
22507         /** @private */
22508     beforeDestroy : function(){
22509
22510     },
22511
22512         /** @private */
22513         onDestroy : function(){
22514
22515     },
22516
22517     /**
22518      * Returns the underlying {@link Roo.Element}.
22519      * @return {Roo.Element} The element
22520      */
22521     getEl : function(){
22522         return this.el;
22523     },
22524
22525     /**
22526      * Returns the id of this component.
22527      * @return {String}
22528      */
22529     getId : function(){
22530         return this.id;
22531     },
22532
22533     /**
22534      * Try to focus this component.
22535      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22536      * @return {Roo.Component} this
22537      */
22538     focus : function(selectText){
22539         if(this.rendered){
22540             this.el.focus();
22541             if(selectText === true){
22542                 this.el.dom.select();
22543             }
22544         }
22545         return this;
22546     },
22547
22548     /** @private */
22549     blur : function(){
22550         if(this.rendered){
22551             this.el.blur();
22552         }
22553         return this;
22554     },
22555
22556     /**
22557      * Disable this component.
22558      * @return {Roo.Component} this
22559      */
22560     disable : function(){
22561         if(this.rendered){
22562             this.onDisable();
22563         }
22564         this.disabled = true;
22565         this.fireEvent("disable", this);
22566         return this;
22567     },
22568
22569         // private
22570     onDisable : function(){
22571         this.getActionEl().addClass(this.disabledClass);
22572         this.el.dom.disabled = true;
22573     },
22574
22575     /**
22576      * Enable this component.
22577      * @return {Roo.Component} this
22578      */
22579     enable : function(){
22580         if(this.rendered){
22581             this.onEnable();
22582         }
22583         this.disabled = false;
22584         this.fireEvent("enable", this);
22585         return this;
22586     },
22587
22588         // private
22589     onEnable : function(){
22590         this.getActionEl().removeClass(this.disabledClass);
22591         this.el.dom.disabled = false;
22592     },
22593
22594     /**
22595      * Convenience function for setting disabled/enabled by boolean.
22596      * @param {Boolean} disabled
22597      */
22598     setDisabled : function(disabled){
22599         this[disabled ? "disable" : "enable"]();
22600     },
22601
22602     /**
22603      * Show this component.
22604      * @return {Roo.Component} this
22605      */
22606     show: function(){
22607         if(this.fireEvent("beforeshow", this) !== false){
22608             this.hidden = false;
22609             if(this.rendered){
22610                 this.onShow();
22611             }
22612             this.fireEvent("show", this);
22613         }
22614         return this;
22615     },
22616
22617     // private
22618     onShow : function(){
22619         var ae = this.getActionEl();
22620         if(this.hideMode == 'visibility'){
22621             ae.dom.style.visibility = "visible";
22622         }else if(this.hideMode == 'offsets'){
22623             ae.removeClass('x-hidden');
22624         }else{
22625             ae.dom.style.display = "";
22626         }
22627     },
22628
22629     /**
22630      * Hide this component.
22631      * @return {Roo.Component} this
22632      */
22633     hide: function(){
22634         if(this.fireEvent("beforehide", this) !== false){
22635             this.hidden = true;
22636             if(this.rendered){
22637                 this.onHide();
22638             }
22639             this.fireEvent("hide", this);
22640         }
22641         return this;
22642     },
22643
22644     // private
22645     onHide : function(){
22646         var ae = this.getActionEl();
22647         if(this.hideMode == 'visibility'){
22648             ae.dom.style.visibility = "hidden";
22649         }else if(this.hideMode == 'offsets'){
22650             ae.addClass('x-hidden');
22651         }else{
22652             ae.dom.style.display = "none";
22653         }
22654     },
22655
22656     /**
22657      * Convenience function to hide or show this component by boolean.
22658      * @param {Boolean} visible True to show, false to hide
22659      * @return {Roo.Component} this
22660      */
22661     setVisible: function(visible){
22662         if(visible) {
22663             this.show();
22664         }else{
22665             this.hide();
22666         }
22667         return this;
22668     },
22669
22670     /**
22671      * Returns true if this component is visible.
22672      */
22673     isVisible : function(){
22674         return this.getActionEl().isVisible();
22675     },
22676
22677     cloneConfig : function(overrides){
22678         overrides = overrides || {};
22679         var id = overrides.id || Roo.id();
22680         var cfg = Roo.applyIf(overrides, this.initialConfig);
22681         cfg.id = id; // prevent dup id
22682         return new this.constructor(cfg);
22683     }
22684 });/*
22685  * Based on:
22686  * Ext JS Library 1.1.1
22687  * Copyright(c) 2006-2007, Ext JS, LLC.
22688  *
22689  * Originally Released Under LGPL - original licence link has changed is not relivant.
22690  *
22691  * Fork - LGPL
22692  * <script type="text/javascript">
22693  */
22694  (function(){ 
22695 /**
22696  * @class Roo.Layer
22697  * @extends Roo.Element
22698  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22699  * automatic maintaining of shadow/shim positions.
22700  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22701  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22702  * you can pass a string with a CSS class name. False turns off the shadow.
22703  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22704  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22705  * @cfg {String} cls CSS class to add to the element
22706  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22707  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22708  * @constructor
22709  * @param {Object} config An object with config options.
22710  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22711  */
22712
22713 Roo.Layer = function(config, existingEl){
22714     config = config || {};
22715     var dh = Roo.DomHelper;
22716     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22717     if(existingEl){
22718         this.dom = Roo.getDom(existingEl);
22719     }
22720     if(!this.dom){
22721         var o = config.dh || {tag: "div", cls: "x-layer"};
22722         this.dom = dh.append(pel, o);
22723     }
22724     if(config.cls){
22725         this.addClass(config.cls);
22726     }
22727     this.constrain = config.constrain !== false;
22728     this.visibilityMode = Roo.Element.VISIBILITY;
22729     if(config.id){
22730         this.id = this.dom.id = config.id;
22731     }else{
22732         this.id = Roo.id(this.dom);
22733     }
22734     this.zindex = config.zindex || this.getZIndex();
22735     this.position("absolute", this.zindex);
22736     if(config.shadow){
22737         this.shadowOffset = config.shadowOffset || 4;
22738         this.shadow = new Roo.Shadow({
22739             offset : this.shadowOffset,
22740             mode : config.shadow
22741         });
22742     }else{
22743         this.shadowOffset = 0;
22744     }
22745     this.useShim = config.shim !== false && Roo.useShims;
22746     this.useDisplay = config.useDisplay;
22747     this.hide();
22748 };
22749
22750 var supr = Roo.Element.prototype;
22751
22752 // shims are shared among layer to keep from having 100 iframes
22753 var shims = [];
22754
22755 Roo.extend(Roo.Layer, Roo.Element, {
22756
22757     getZIndex : function(){
22758         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22759     },
22760
22761     getShim : function(){
22762         if(!this.useShim){
22763             return null;
22764         }
22765         if(this.shim){
22766             return this.shim;
22767         }
22768         var shim = shims.shift();
22769         if(!shim){
22770             shim = this.createShim();
22771             shim.enableDisplayMode('block');
22772             shim.dom.style.display = 'none';
22773             shim.dom.style.visibility = 'visible';
22774         }
22775         var pn = this.dom.parentNode;
22776         if(shim.dom.parentNode != pn){
22777             pn.insertBefore(shim.dom, this.dom);
22778         }
22779         shim.setStyle('z-index', this.getZIndex()-2);
22780         this.shim = shim;
22781         return shim;
22782     },
22783
22784     hideShim : function(){
22785         if(this.shim){
22786             this.shim.setDisplayed(false);
22787             shims.push(this.shim);
22788             delete this.shim;
22789         }
22790     },
22791
22792     disableShadow : function(){
22793         if(this.shadow){
22794             this.shadowDisabled = true;
22795             this.shadow.hide();
22796             this.lastShadowOffset = this.shadowOffset;
22797             this.shadowOffset = 0;
22798         }
22799     },
22800
22801     enableShadow : function(show){
22802         if(this.shadow){
22803             this.shadowDisabled = false;
22804             this.shadowOffset = this.lastShadowOffset;
22805             delete this.lastShadowOffset;
22806             if(show){
22807                 this.sync(true);
22808             }
22809         }
22810     },
22811
22812     // private
22813     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22814     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22815     sync : function(doShow){
22816         var sw = this.shadow;
22817         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22818             var sh = this.getShim();
22819
22820             var w = this.getWidth(),
22821                 h = this.getHeight();
22822
22823             var l = this.getLeft(true),
22824                 t = this.getTop(true);
22825
22826             if(sw && !this.shadowDisabled){
22827                 if(doShow && !sw.isVisible()){
22828                     sw.show(this);
22829                 }else{
22830                     sw.realign(l, t, w, h);
22831                 }
22832                 if(sh){
22833                     if(doShow){
22834                        sh.show();
22835                     }
22836                     // fit the shim behind the shadow, so it is shimmed too
22837                     var a = sw.adjusts, s = sh.dom.style;
22838                     s.left = (Math.min(l, l+a.l))+"px";
22839                     s.top = (Math.min(t, t+a.t))+"px";
22840                     s.width = (w+a.w)+"px";
22841                     s.height = (h+a.h)+"px";
22842                 }
22843             }else if(sh){
22844                 if(doShow){
22845                    sh.show();
22846                 }
22847                 sh.setSize(w, h);
22848                 sh.setLeftTop(l, t);
22849             }
22850             
22851         }
22852     },
22853
22854     // private
22855     destroy : function(){
22856         this.hideShim();
22857         if(this.shadow){
22858             this.shadow.hide();
22859         }
22860         this.removeAllListeners();
22861         var pn = this.dom.parentNode;
22862         if(pn){
22863             pn.removeChild(this.dom);
22864         }
22865         Roo.Element.uncache(this.id);
22866     },
22867
22868     remove : function(){
22869         this.destroy();
22870     },
22871
22872     // private
22873     beginUpdate : function(){
22874         this.updating = true;
22875     },
22876
22877     // private
22878     endUpdate : function(){
22879         this.updating = false;
22880         this.sync(true);
22881     },
22882
22883     // private
22884     hideUnders : function(negOffset){
22885         if(this.shadow){
22886             this.shadow.hide();
22887         }
22888         this.hideShim();
22889     },
22890
22891     // private
22892     constrainXY : function(){
22893         if(this.constrain){
22894             var vw = Roo.lib.Dom.getViewWidth(),
22895                 vh = Roo.lib.Dom.getViewHeight();
22896             var s = Roo.get(document).getScroll();
22897
22898             var xy = this.getXY();
22899             var x = xy[0], y = xy[1];   
22900             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22901             // only move it if it needs it
22902             var moved = false;
22903             // first validate right/bottom
22904             if((x + w) > vw+s.left){
22905                 x = vw - w - this.shadowOffset;
22906                 moved = true;
22907             }
22908             if((y + h) > vh+s.top){
22909                 y = vh - h - this.shadowOffset;
22910                 moved = true;
22911             }
22912             // then make sure top/left isn't negative
22913             if(x < s.left){
22914                 x = s.left;
22915                 moved = true;
22916             }
22917             if(y < s.top){
22918                 y = s.top;
22919                 moved = true;
22920             }
22921             if(moved){
22922                 if(this.avoidY){
22923                     var ay = this.avoidY;
22924                     if(y <= ay && (y+h) >= ay){
22925                         y = ay-h-5;   
22926                     }
22927                 }
22928                 xy = [x, y];
22929                 this.storeXY(xy);
22930                 supr.setXY.call(this, xy);
22931                 this.sync();
22932             }
22933         }
22934     },
22935
22936     isVisible : function(){
22937         return this.visible;    
22938     },
22939
22940     // private
22941     showAction : function(){
22942         this.visible = true; // track visibility to prevent getStyle calls
22943         if(this.useDisplay === true){
22944             this.setDisplayed("");
22945         }else if(this.lastXY){
22946             supr.setXY.call(this, this.lastXY);
22947         }else if(this.lastLT){
22948             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22949         }
22950     },
22951
22952     // private
22953     hideAction : function(){
22954         this.visible = false;
22955         if(this.useDisplay === true){
22956             this.setDisplayed(false);
22957         }else{
22958             this.setLeftTop(-10000,-10000);
22959         }
22960     },
22961
22962     // overridden Element method
22963     setVisible : function(v, a, d, c, e){
22964         if(v){
22965             this.showAction();
22966         }
22967         if(a && v){
22968             var cb = function(){
22969                 this.sync(true);
22970                 if(c){
22971                     c();
22972                 }
22973             }.createDelegate(this);
22974             supr.setVisible.call(this, true, true, d, cb, e);
22975         }else{
22976             if(!v){
22977                 this.hideUnders(true);
22978             }
22979             var cb = c;
22980             if(a){
22981                 cb = function(){
22982                     this.hideAction();
22983                     if(c){
22984                         c();
22985                     }
22986                 }.createDelegate(this);
22987             }
22988             supr.setVisible.call(this, v, a, d, cb, e);
22989             if(v){
22990                 this.sync(true);
22991             }else if(!a){
22992                 this.hideAction();
22993             }
22994         }
22995     },
22996
22997     storeXY : function(xy){
22998         delete this.lastLT;
22999         this.lastXY = xy;
23000     },
23001
23002     storeLeftTop : function(left, top){
23003         delete this.lastXY;
23004         this.lastLT = [left, top];
23005     },
23006
23007     // private
23008     beforeFx : function(){
23009         this.beforeAction();
23010         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23011     },
23012
23013     // private
23014     afterFx : function(){
23015         Roo.Layer.superclass.afterFx.apply(this, arguments);
23016         this.sync(this.isVisible());
23017     },
23018
23019     // private
23020     beforeAction : function(){
23021         if(!this.updating && this.shadow){
23022             this.shadow.hide();
23023         }
23024     },
23025
23026     // overridden Element method
23027     setLeft : function(left){
23028         this.storeLeftTop(left, this.getTop(true));
23029         supr.setLeft.apply(this, arguments);
23030         this.sync();
23031     },
23032
23033     setTop : function(top){
23034         this.storeLeftTop(this.getLeft(true), top);
23035         supr.setTop.apply(this, arguments);
23036         this.sync();
23037     },
23038
23039     setLeftTop : function(left, top){
23040         this.storeLeftTop(left, top);
23041         supr.setLeftTop.apply(this, arguments);
23042         this.sync();
23043     },
23044
23045     setXY : function(xy, a, d, c, e){
23046         this.fixDisplay();
23047         this.beforeAction();
23048         this.storeXY(xy);
23049         var cb = this.createCB(c);
23050         supr.setXY.call(this, xy, a, d, cb, e);
23051         if(!a){
23052             cb();
23053         }
23054     },
23055
23056     // private
23057     createCB : function(c){
23058         var el = this;
23059         return function(){
23060             el.constrainXY();
23061             el.sync(true);
23062             if(c){
23063                 c();
23064             }
23065         };
23066     },
23067
23068     // overridden Element method
23069     setX : function(x, a, d, c, e){
23070         this.setXY([x, this.getY()], a, d, c, e);
23071     },
23072
23073     // overridden Element method
23074     setY : function(y, a, d, c, e){
23075         this.setXY([this.getX(), y], a, d, c, e);
23076     },
23077
23078     // overridden Element method
23079     setSize : function(w, h, a, d, c, e){
23080         this.beforeAction();
23081         var cb = this.createCB(c);
23082         supr.setSize.call(this, w, h, a, d, cb, e);
23083         if(!a){
23084             cb();
23085         }
23086     },
23087
23088     // overridden Element method
23089     setWidth : function(w, a, d, c, e){
23090         this.beforeAction();
23091         var cb = this.createCB(c);
23092         supr.setWidth.call(this, w, a, d, cb, e);
23093         if(!a){
23094             cb();
23095         }
23096     },
23097
23098     // overridden Element method
23099     setHeight : function(h, a, d, c, e){
23100         this.beforeAction();
23101         var cb = this.createCB(c);
23102         supr.setHeight.call(this, h, a, d, cb, e);
23103         if(!a){
23104             cb();
23105         }
23106     },
23107
23108     // overridden Element method
23109     setBounds : function(x, y, w, h, a, d, c, e){
23110         this.beforeAction();
23111         var cb = this.createCB(c);
23112         if(!a){
23113             this.storeXY([x, y]);
23114             supr.setXY.call(this, [x, y]);
23115             supr.setSize.call(this, w, h, a, d, cb, e);
23116             cb();
23117         }else{
23118             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
23119         }
23120         return this;
23121     },
23122     
23123     /**
23124      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
23125      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
23126      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
23127      * @param {Number} zindex The new z-index to set
23128      * @return {this} The Layer
23129      */
23130     setZIndex : function(zindex){
23131         this.zindex = zindex;
23132         this.setStyle("z-index", zindex + 2);
23133         if(this.shadow){
23134             this.shadow.setZIndex(zindex + 1);
23135         }
23136         if(this.shim){
23137             this.shim.setStyle("z-index", zindex);
23138         }
23139     }
23140 });
23141 })();/*
23142  * Based on:
23143  * Ext JS Library 1.1.1
23144  * Copyright(c) 2006-2007, Ext JS, LLC.
23145  *
23146  * Originally Released Under LGPL - original licence link has changed is not relivant.
23147  *
23148  * Fork - LGPL
23149  * <script type="text/javascript">
23150  */
23151
23152
23153 /**
23154  * @class Roo.Shadow
23155  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
23156  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
23157  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
23158  * @constructor
23159  * Create a new Shadow
23160  * @param {Object} config The config object
23161  */
23162 Roo.Shadow = function(config){
23163     Roo.apply(this, config);
23164     if(typeof this.mode != "string"){
23165         this.mode = this.defaultMode;
23166     }
23167     var o = this.offset, a = {h: 0};
23168     var rad = Math.floor(this.offset/2);
23169     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
23170         case "drop":
23171             a.w = 0;
23172             a.l = a.t = o;
23173             a.t -= 1;
23174             if(Roo.isIE){
23175                 a.l -= this.offset + rad;
23176                 a.t -= this.offset + rad;
23177                 a.w -= rad;
23178                 a.h -= rad;
23179                 a.t += 1;
23180             }
23181         break;
23182         case "sides":
23183             a.w = (o*2);
23184             a.l = -o;
23185             a.t = o-1;
23186             if(Roo.isIE){
23187                 a.l -= (this.offset - rad);
23188                 a.t -= this.offset + rad;
23189                 a.l += 1;
23190                 a.w -= (this.offset - rad)*2;
23191                 a.w -= rad + 1;
23192                 a.h -= 1;
23193             }
23194         break;
23195         case "frame":
23196             a.w = a.h = (o*2);
23197             a.l = a.t = -o;
23198             a.t += 1;
23199             a.h -= 2;
23200             if(Roo.isIE){
23201                 a.l -= (this.offset - rad);
23202                 a.t -= (this.offset - rad);
23203                 a.l += 1;
23204                 a.w -= (this.offset + rad + 1);
23205                 a.h -= (this.offset + rad);
23206                 a.h += 1;
23207             }
23208         break;
23209     };
23210
23211     this.adjusts = a;
23212 };
23213
23214 Roo.Shadow.prototype = {
23215     /**
23216      * @cfg {String} mode
23217      * The shadow display mode.  Supports the following options:<br />
23218      * sides: Shadow displays on both sides and bottom only<br />
23219      * frame: Shadow displays equally on all four sides<br />
23220      * drop: Traditional bottom-right drop shadow (default)
23221      */
23222     /**
23223      * @cfg {String} offset
23224      * The number of pixels to offset the shadow from the element (defaults to 4)
23225      */
23226     offset: 4,
23227
23228     // private
23229     defaultMode: "drop",
23230
23231     /**
23232      * Displays the shadow under the target element
23233      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
23234      */
23235     show : function(target){
23236         target = Roo.get(target);
23237         if(!this.el){
23238             this.el = Roo.Shadow.Pool.pull();
23239             if(this.el.dom.nextSibling != target.dom){
23240                 this.el.insertBefore(target);
23241             }
23242         }
23243         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
23244         if(Roo.isIE){
23245             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
23246         }
23247         this.realign(
23248             target.getLeft(true),
23249             target.getTop(true),
23250             target.getWidth(),
23251             target.getHeight()
23252         );
23253         this.el.dom.style.display = "block";
23254     },
23255
23256     /**
23257      * Returns true if the shadow is visible, else false
23258      */
23259     isVisible : function(){
23260         return this.el ? true : false;  
23261     },
23262
23263     /**
23264      * Direct alignment when values are already available. Show must be called at least once before
23265      * calling this method to ensure it is initialized.
23266      * @param {Number} left The target element left position
23267      * @param {Number} top The target element top position
23268      * @param {Number} width The target element width
23269      * @param {Number} height The target element height
23270      */
23271     realign : function(l, t, w, h){
23272         if(!this.el){
23273             return;
23274         }
23275         var a = this.adjusts, d = this.el.dom, s = d.style;
23276         var iea = 0;
23277         s.left = (l+a.l)+"px";
23278         s.top = (t+a.t)+"px";
23279         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
23280  
23281         if(s.width != sws || s.height != shs){
23282             s.width = sws;
23283             s.height = shs;
23284             if(!Roo.isIE){
23285                 var cn = d.childNodes;
23286                 var sww = Math.max(0, (sw-12))+"px";
23287                 cn[0].childNodes[1].style.width = sww;
23288                 cn[1].childNodes[1].style.width = sww;
23289                 cn[2].childNodes[1].style.width = sww;
23290                 cn[1].style.height = Math.max(0, (sh-12))+"px";
23291             }
23292         }
23293     },
23294
23295     /**
23296      * Hides this shadow
23297      */
23298     hide : function(){
23299         if(this.el){
23300             this.el.dom.style.display = "none";
23301             Roo.Shadow.Pool.push(this.el);
23302             delete this.el;
23303         }
23304     },
23305
23306     /**
23307      * Adjust the z-index of this shadow
23308      * @param {Number} zindex The new z-index
23309      */
23310     setZIndex : function(z){
23311         this.zIndex = z;
23312         if(this.el){
23313             this.el.setStyle("z-index", z);
23314         }
23315     }
23316 };
23317
23318 // Private utility class that manages the internal Shadow cache
23319 Roo.Shadow.Pool = function(){
23320     var p = [];
23321     var markup = Roo.isIE ?
23322                  '<div class="x-ie-shadow"></div>' :
23323                  '<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>';
23324     return {
23325         pull : function(){
23326             var sh = p.shift();
23327             if(!sh){
23328                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
23329                 sh.autoBoxAdjust = false;
23330             }
23331             return sh;
23332         },
23333
23334         push : function(sh){
23335             p.push(sh);
23336         }
23337     };
23338 }();/*
23339  * Based on:
23340  * Ext JS Library 1.1.1
23341  * Copyright(c) 2006-2007, Ext JS, LLC.
23342  *
23343  * Originally Released Under LGPL - original licence link has changed is not relivant.
23344  *
23345  * Fork - LGPL
23346  * <script type="text/javascript">
23347  */
23348
23349 /**
23350  * @class Roo.BoxComponent
23351  * @extends Roo.Component
23352  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
23353  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
23354  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
23355  * layout containers.
23356  * @constructor
23357  * @param {Roo.Element/String/Object} config The configuration options.
23358  */
23359 Roo.BoxComponent = function(config){
23360     Roo.Component.call(this, config);
23361     this.addEvents({
23362         /**
23363          * @event resize
23364          * Fires after the component is resized.
23365              * @param {Roo.Component} this
23366              * @param {Number} adjWidth The box-adjusted width that was set
23367              * @param {Number} adjHeight The box-adjusted height that was set
23368              * @param {Number} rawWidth The width that was originally specified
23369              * @param {Number} rawHeight The height that was originally specified
23370              */
23371         resize : true,
23372         /**
23373          * @event move
23374          * Fires after the component is moved.
23375              * @param {Roo.Component} this
23376              * @param {Number} x The new x position
23377              * @param {Number} y The new y position
23378              */
23379         move : true
23380     });
23381 };
23382
23383 Roo.extend(Roo.BoxComponent, Roo.Component, {
23384     // private, set in afterRender to signify that the component has been rendered
23385     boxReady : false,
23386     // private, used to defer height settings to subclasses
23387     deferHeight: false,
23388     /** @cfg {Number} width
23389      * width (optional) size of component
23390      */
23391      /** @cfg {Number} height
23392      * height (optional) size of component
23393      */
23394      
23395     /**
23396      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
23397      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
23398      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
23399      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
23400      * @return {Roo.BoxComponent} this
23401      */
23402     setSize : function(w, h){
23403         // support for standard size objects
23404         if(typeof w == 'object'){
23405             h = w.height;
23406             w = w.width;
23407         }
23408         // not rendered
23409         if(!this.boxReady){
23410             this.width = w;
23411             this.height = h;
23412             return this;
23413         }
23414
23415         // prevent recalcs when not needed
23416         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
23417             return this;
23418         }
23419         this.lastSize = {width: w, height: h};
23420
23421         var adj = this.adjustSize(w, h);
23422         var aw = adj.width, ah = adj.height;
23423         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
23424             var rz = this.getResizeEl();
23425             if(!this.deferHeight && aw !== undefined && ah !== undefined){
23426                 rz.setSize(aw, ah);
23427             }else if(!this.deferHeight && ah !== undefined){
23428                 rz.setHeight(ah);
23429             }else if(aw !== undefined){
23430                 rz.setWidth(aw);
23431             }
23432             this.onResize(aw, ah, w, h);
23433             this.fireEvent('resize', this, aw, ah, w, h);
23434         }
23435         return this;
23436     },
23437
23438     /**
23439      * Gets the current size of the component's underlying element.
23440      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
23441      */
23442     getSize : function(){
23443         return this.el.getSize();
23444     },
23445
23446     /**
23447      * Gets the current XY position of the component's underlying element.
23448      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23449      * @return {Array} The XY position of the element (e.g., [100, 200])
23450      */
23451     getPosition : function(local){
23452         if(local === true){
23453             return [this.el.getLeft(true), this.el.getTop(true)];
23454         }
23455         return this.xy || this.el.getXY();
23456     },
23457
23458     /**
23459      * Gets the current box measurements of the component's underlying element.
23460      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23461      * @returns {Object} box An object in the format {x, y, width, height}
23462      */
23463     getBox : function(local){
23464         var s = this.el.getSize();
23465         if(local){
23466             s.x = this.el.getLeft(true);
23467             s.y = this.el.getTop(true);
23468         }else{
23469             var xy = this.xy || this.el.getXY();
23470             s.x = xy[0];
23471             s.y = xy[1];
23472         }
23473         return s;
23474     },
23475
23476     /**
23477      * Sets the current box measurements of the component's underlying element.
23478      * @param {Object} box An object in the format {x, y, width, height}
23479      * @returns {Roo.BoxComponent} this
23480      */
23481     updateBox : function(box){
23482         this.setSize(box.width, box.height);
23483         this.setPagePosition(box.x, box.y);
23484         return this;
23485     },
23486
23487     // protected
23488     getResizeEl : function(){
23489         return this.resizeEl || this.el;
23490     },
23491
23492     // protected
23493     getPositionEl : function(){
23494         return this.positionEl || this.el;
23495     },
23496
23497     /**
23498      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23499      * This method fires the move event.
23500      * @param {Number} left The new left
23501      * @param {Number} top The new top
23502      * @returns {Roo.BoxComponent} this
23503      */
23504     setPosition : function(x, y){
23505         this.x = x;
23506         this.y = y;
23507         if(!this.boxReady){
23508             return this;
23509         }
23510         var adj = this.adjustPosition(x, y);
23511         var ax = adj.x, ay = adj.y;
23512
23513         var el = this.getPositionEl();
23514         if(ax !== undefined || ay !== undefined){
23515             if(ax !== undefined && ay !== undefined){
23516                 el.setLeftTop(ax, ay);
23517             }else if(ax !== undefined){
23518                 el.setLeft(ax);
23519             }else if(ay !== undefined){
23520                 el.setTop(ay);
23521             }
23522             this.onPosition(ax, ay);
23523             this.fireEvent('move', this, ax, ay);
23524         }
23525         return this;
23526     },
23527
23528     /**
23529      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23530      * This method fires the move event.
23531      * @param {Number} x The new x position
23532      * @param {Number} y The new y position
23533      * @returns {Roo.BoxComponent} this
23534      */
23535     setPagePosition : function(x, y){
23536         this.pageX = x;
23537         this.pageY = y;
23538         if(!this.boxReady){
23539             return;
23540         }
23541         if(x === undefined || y === undefined){ // cannot translate undefined points
23542             return;
23543         }
23544         var p = this.el.translatePoints(x, y);
23545         this.setPosition(p.left, p.top);
23546         return this;
23547     },
23548
23549     // private
23550     onRender : function(ct, position){
23551         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23552         if(this.resizeEl){
23553             this.resizeEl = Roo.get(this.resizeEl);
23554         }
23555         if(this.positionEl){
23556             this.positionEl = Roo.get(this.positionEl);
23557         }
23558     },
23559
23560     // private
23561     afterRender : function(){
23562         Roo.BoxComponent.superclass.afterRender.call(this);
23563         this.boxReady = true;
23564         this.setSize(this.width, this.height);
23565         if(this.x || this.y){
23566             this.setPosition(this.x, this.y);
23567         }
23568         if(this.pageX || this.pageY){
23569             this.setPagePosition(this.pageX, this.pageY);
23570         }
23571     },
23572
23573     /**
23574      * Force the component's size to recalculate based on the underlying element's current height and width.
23575      * @returns {Roo.BoxComponent} this
23576      */
23577     syncSize : function(){
23578         delete this.lastSize;
23579         this.setSize(this.el.getWidth(), this.el.getHeight());
23580         return this;
23581     },
23582
23583     /**
23584      * Called after the component is resized, this method is empty by default but can be implemented by any
23585      * subclass that needs to perform custom logic after a resize occurs.
23586      * @param {Number} adjWidth The box-adjusted width that was set
23587      * @param {Number} adjHeight The box-adjusted height that was set
23588      * @param {Number} rawWidth The width that was originally specified
23589      * @param {Number} rawHeight The height that was originally specified
23590      */
23591     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23592
23593     },
23594
23595     /**
23596      * Called after the component is moved, this method is empty by default but can be implemented by any
23597      * subclass that needs to perform custom logic after a move occurs.
23598      * @param {Number} x The new x position
23599      * @param {Number} y The new y position
23600      */
23601     onPosition : function(x, y){
23602
23603     },
23604
23605     // private
23606     adjustSize : function(w, h){
23607         if(this.autoWidth){
23608             w = 'auto';
23609         }
23610         if(this.autoHeight){
23611             h = 'auto';
23612         }
23613         return {width : w, height: h};
23614     },
23615
23616     // private
23617     adjustPosition : function(x, y){
23618         return {x : x, y: y};
23619     }
23620 });/*
23621  * Based on:
23622  * Ext JS Library 1.1.1
23623  * Copyright(c) 2006-2007, Ext JS, LLC.
23624  *
23625  * Originally Released Under LGPL - original licence link has changed is not relivant.
23626  *
23627  * Fork - LGPL
23628  * <script type="text/javascript">
23629  */
23630
23631
23632 /**
23633  * @class Roo.SplitBar
23634  * @extends Roo.util.Observable
23635  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23636  * <br><br>
23637  * Usage:
23638  * <pre><code>
23639 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23640                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23641 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23642 split.minSize = 100;
23643 split.maxSize = 600;
23644 split.animate = true;
23645 split.on('moved', splitterMoved);
23646 </code></pre>
23647  * @constructor
23648  * Create a new SplitBar
23649  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23650  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23651  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23652  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23653                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23654                         position of the SplitBar).
23655  */
23656 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23657     
23658     /** @private */
23659     this.el = Roo.get(dragElement, true);
23660     this.el.dom.unselectable = "on";
23661     /** @private */
23662     this.resizingEl = Roo.get(resizingElement, true);
23663
23664     /**
23665      * @private
23666      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23667      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23668      * @type Number
23669      */
23670     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23671     
23672     /**
23673      * The minimum size of the resizing element. (Defaults to 0)
23674      * @type Number
23675      */
23676     this.minSize = 0;
23677     
23678     /**
23679      * The maximum size of the resizing element. (Defaults to 2000)
23680      * @type Number
23681      */
23682     this.maxSize = 2000;
23683     
23684     /**
23685      * Whether to animate the transition to the new size
23686      * @type Boolean
23687      */
23688     this.animate = false;
23689     
23690     /**
23691      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23692      * @type Boolean
23693      */
23694     this.useShim = false;
23695     
23696     /** @private */
23697     this.shim = null;
23698     
23699     if(!existingProxy){
23700         /** @private */
23701         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23702     }else{
23703         this.proxy = Roo.get(existingProxy).dom;
23704     }
23705     /** @private */
23706     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23707     
23708     /** @private */
23709     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23710     
23711     /** @private */
23712     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23713     
23714     /** @private */
23715     this.dragSpecs = {};
23716     
23717     /**
23718      * @private The adapter to use to positon and resize elements
23719      */
23720     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23721     this.adapter.init(this);
23722     
23723     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23724         /** @private */
23725         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23726         this.el.addClass("x-splitbar-h");
23727     }else{
23728         /** @private */
23729         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23730         this.el.addClass("x-splitbar-v");
23731     }
23732     
23733     this.addEvents({
23734         /**
23735          * @event resize
23736          * Fires when the splitter is moved (alias for {@link #event-moved})
23737          * @param {Roo.SplitBar} this
23738          * @param {Number} newSize the new width or height
23739          */
23740         "resize" : true,
23741         /**
23742          * @event moved
23743          * Fires when the splitter is moved
23744          * @param {Roo.SplitBar} this
23745          * @param {Number} newSize the new width or height
23746          */
23747         "moved" : true,
23748         /**
23749          * @event beforeresize
23750          * Fires before the splitter is dragged
23751          * @param {Roo.SplitBar} this
23752          */
23753         "beforeresize" : true,
23754
23755         "beforeapply" : true
23756     });
23757
23758     Roo.util.Observable.call(this);
23759 };
23760
23761 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23762     onStartProxyDrag : function(x, y){
23763         this.fireEvent("beforeresize", this);
23764         if(!this.overlay){
23765             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23766             o.unselectable();
23767             o.enableDisplayMode("block");
23768             // all splitbars share the same overlay
23769             Roo.SplitBar.prototype.overlay = o;
23770         }
23771         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23772         this.overlay.show();
23773         Roo.get(this.proxy).setDisplayed("block");
23774         var size = this.adapter.getElementSize(this);
23775         this.activeMinSize = this.getMinimumSize();;
23776         this.activeMaxSize = this.getMaximumSize();;
23777         var c1 = size - this.activeMinSize;
23778         var c2 = Math.max(this.activeMaxSize - size, 0);
23779         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23780             this.dd.resetConstraints();
23781             this.dd.setXConstraint(
23782                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23783                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23784             );
23785             this.dd.setYConstraint(0, 0);
23786         }else{
23787             this.dd.resetConstraints();
23788             this.dd.setXConstraint(0, 0);
23789             this.dd.setYConstraint(
23790                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23791                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23792             );
23793          }
23794         this.dragSpecs.startSize = size;
23795         this.dragSpecs.startPoint = [x, y];
23796         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23797     },
23798     
23799     /** 
23800      * @private Called after the drag operation by the DDProxy
23801      */
23802     onEndProxyDrag : function(e){
23803         Roo.get(this.proxy).setDisplayed(false);
23804         var endPoint = Roo.lib.Event.getXY(e);
23805         if(this.overlay){
23806             this.overlay.hide();
23807         }
23808         var newSize;
23809         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23810             newSize = this.dragSpecs.startSize + 
23811                 (this.placement == Roo.SplitBar.LEFT ?
23812                     endPoint[0] - this.dragSpecs.startPoint[0] :
23813                     this.dragSpecs.startPoint[0] - endPoint[0]
23814                 );
23815         }else{
23816             newSize = this.dragSpecs.startSize + 
23817                 (this.placement == Roo.SplitBar.TOP ?
23818                     endPoint[1] - this.dragSpecs.startPoint[1] :
23819                     this.dragSpecs.startPoint[1] - endPoint[1]
23820                 );
23821         }
23822         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23823         if(newSize != this.dragSpecs.startSize){
23824             if(this.fireEvent('beforeapply', this, newSize) !== false){
23825                 this.adapter.setElementSize(this, newSize);
23826                 this.fireEvent("moved", this, newSize);
23827                 this.fireEvent("resize", this, newSize);
23828             }
23829         }
23830     },
23831     
23832     /**
23833      * Get the adapter this SplitBar uses
23834      * @return The adapter object
23835      */
23836     getAdapter : function(){
23837         return this.adapter;
23838     },
23839     
23840     /**
23841      * Set the adapter this SplitBar uses
23842      * @param {Object} adapter A SplitBar adapter object
23843      */
23844     setAdapter : function(adapter){
23845         this.adapter = adapter;
23846         this.adapter.init(this);
23847     },
23848     
23849     /**
23850      * Gets the minimum size for the resizing element
23851      * @return {Number} The minimum size
23852      */
23853     getMinimumSize : function(){
23854         return this.minSize;
23855     },
23856     
23857     /**
23858      * Sets the minimum size for the resizing element
23859      * @param {Number} minSize The minimum size
23860      */
23861     setMinimumSize : function(minSize){
23862         this.minSize = minSize;
23863     },
23864     
23865     /**
23866      * Gets the maximum size for the resizing element
23867      * @return {Number} The maximum size
23868      */
23869     getMaximumSize : function(){
23870         return this.maxSize;
23871     },
23872     
23873     /**
23874      * Sets the maximum size for the resizing element
23875      * @param {Number} maxSize The maximum size
23876      */
23877     setMaximumSize : function(maxSize){
23878         this.maxSize = maxSize;
23879     },
23880     
23881     /**
23882      * Sets the initialize size for the resizing element
23883      * @param {Number} size The initial size
23884      */
23885     setCurrentSize : function(size){
23886         var oldAnimate = this.animate;
23887         this.animate = false;
23888         this.adapter.setElementSize(this, size);
23889         this.animate = oldAnimate;
23890     },
23891     
23892     /**
23893      * Destroy this splitbar. 
23894      * @param {Boolean} removeEl True to remove the element
23895      */
23896     destroy : function(removeEl){
23897         if(this.shim){
23898             this.shim.remove();
23899         }
23900         this.dd.unreg();
23901         this.proxy.parentNode.removeChild(this.proxy);
23902         if(removeEl){
23903             this.el.remove();
23904         }
23905     }
23906 });
23907
23908 /**
23909  * @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.
23910  */
23911 Roo.SplitBar.createProxy = function(dir){
23912     var proxy = new Roo.Element(document.createElement("div"));
23913     proxy.unselectable();
23914     var cls = 'x-splitbar-proxy';
23915     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23916     document.body.appendChild(proxy.dom);
23917     return proxy.dom;
23918 };
23919
23920 /** 
23921  * @class Roo.SplitBar.BasicLayoutAdapter
23922  * Default Adapter. It assumes the splitter and resizing element are not positioned
23923  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23924  */
23925 Roo.SplitBar.BasicLayoutAdapter = function(){
23926 };
23927
23928 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23929     // do nothing for now
23930     init : function(s){
23931     
23932     },
23933     /**
23934      * Called before drag operations to get the current size of the resizing element. 
23935      * @param {Roo.SplitBar} s The SplitBar using this adapter
23936      */
23937      getElementSize : function(s){
23938         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23939             return s.resizingEl.getWidth();
23940         }else{
23941             return s.resizingEl.getHeight();
23942         }
23943     },
23944     
23945     /**
23946      * Called after drag operations to set the size of the resizing element.
23947      * @param {Roo.SplitBar} s The SplitBar using this adapter
23948      * @param {Number} newSize The new size to set
23949      * @param {Function} onComplete A function to be invoked when resizing is complete
23950      */
23951     setElementSize : function(s, newSize, onComplete){
23952         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23953             if(!s.animate){
23954                 s.resizingEl.setWidth(newSize);
23955                 if(onComplete){
23956                     onComplete(s, newSize);
23957                 }
23958             }else{
23959                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23960             }
23961         }else{
23962             
23963             if(!s.animate){
23964                 s.resizingEl.setHeight(newSize);
23965                 if(onComplete){
23966                     onComplete(s, newSize);
23967                 }
23968             }else{
23969                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23970             }
23971         }
23972     }
23973 };
23974
23975 /** 
23976  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23977  * @extends Roo.SplitBar.BasicLayoutAdapter
23978  * Adapter that  moves the splitter element to align with the resized sizing element. 
23979  * Used with an absolute positioned SplitBar.
23980  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23981  * document.body, make sure you assign an id to the body element.
23982  */
23983 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23984     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23985     this.container = Roo.get(container);
23986 };
23987
23988 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23989     init : function(s){
23990         this.basic.init(s);
23991     },
23992     
23993     getElementSize : function(s){
23994         return this.basic.getElementSize(s);
23995     },
23996     
23997     setElementSize : function(s, newSize, onComplete){
23998         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23999     },
24000     
24001     moveSplitter : function(s){
24002         var yes = Roo.SplitBar;
24003         switch(s.placement){
24004             case yes.LEFT:
24005                 s.el.setX(s.resizingEl.getRight());
24006                 break;
24007             case yes.RIGHT:
24008                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24009                 break;
24010             case yes.TOP:
24011                 s.el.setY(s.resizingEl.getBottom());
24012                 break;
24013             case yes.BOTTOM:
24014                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24015                 break;
24016         }
24017     }
24018 };
24019
24020 /**
24021  * Orientation constant - Create a vertical SplitBar
24022  * @static
24023  * @type Number
24024  */
24025 Roo.SplitBar.VERTICAL = 1;
24026
24027 /**
24028  * Orientation constant - Create a horizontal SplitBar
24029  * @static
24030  * @type Number
24031  */
24032 Roo.SplitBar.HORIZONTAL = 2;
24033
24034 /**
24035  * Placement constant - The resizing element is to the left of the splitter element
24036  * @static
24037  * @type Number
24038  */
24039 Roo.SplitBar.LEFT = 1;
24040
24041 /**
24042  * Placement constant - The resizing element is to the right of the splitter element
24043  * @static
24044  * @type Number
24045  */
24046 Roo.SplitBar.RIGHT = 2;
24047
24048 /**
24049  * Placement constant - The resizing element is positioned above the splitter element
24050  * @static
24051  * @type Number
24052  */
24053 Roo.SplitBar.TOP = 3;
24054
24055 /**
24056  * Placement constant - The resizing element is positioned under splitter element
24057  * @static
24058  * @type Number
24059  */
24060 Roo.SplitBar.BOTTOM = 4;
24061 /*
24062  * Based on:
24063  * Ext JS Library 1.1.1
24064  * Copyright(c) 2006-2007, Ext JS, LLC.
24065  *
24066  * Originally Released Under LGPL - original licence link has changed is not relivant.
24067  *
24068  * Fork - LGPL
24069  * <script type="text/javascript">
24070  */
24071
24072 /**
24073  * @class Roo.View
24074  * @extends Roo.util.Observable
24075  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24076  * This class also supports single and multi selection modes. <br>
24077  * Create a data model bound view:
24078  <pre><code>
24079  var store = new Roo.data.Store(...);
24080
24081  var view = new Roo.View({
24082     el : "my-element",
24083     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24084  
24085     singleSelect: true,
24086     selectedClass: "ydataview-selected",
24087     store: store
24088  });
24089
24090  // listen for node click?
24091  view.on("click", function(vw, index, node, e){
24092  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24093  });
24094
24095  // load XML data
24096  dataModel.load("foobar.xml");
24097  </code></pre>
24098  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24099  * <br><br>
24100  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24101  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24102  * 
24103  * Note: old style constructor is still suported (container, template, config)
24104  * 
24105  * @constructor
24106  * Create a new View
24107  * @param {Object} config The config object
24108  * 
24109  */
24110 Roo.View = function(config, depreciated_tpl, depreciated_config){
24111     
24112     if (typeof(depreciated_tpl) == 'undefined') {
24113         // new way.. - universal constructor.
24114         Roo.apply(this, config);
24115         this.el  = Roo.get(this.el);
24116     } else {
24117         // old format..
24118         this.el  = Roo.get(config);
24119         this.tpl = depreciated_tpl;
24120         Roo.apply(this, depreciated_config);
24121     }
24122     this.wrapEl  = this.el.wrap().wrap();
24123     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24124     
24125     
24126     if(typeof(this.tpl) == "string"){
24127         this.tpl = new Roo.Template(this.tpl);
24128     } else {
24129         // support xtype ctors..
24130         this.tpl = new Roo.factory(this.tpl, Roo);
24131     }
24132     
24133     
24134     this.tpl.compile();
24135    
24136   
24137     
24138      
24139     /** @private */
24140     this.addEvents({
24141         /**
24142          * @event beforeclick
24143          * Fires before a click is processed. Returns false to cancel the default action.
24144          * @param {Roo.View} this
24145          * @param {Number} index The index of the target node
24146          * @param {HTMLElement} node The target node
24147          * @param {Roo.EventObject} e The raw event object
24148          */
24149             "beforeclick" : true,
24150         /**
24151          * @event click
24152          * Fires when a template node is clicked.
24153          * @param {Roo.View} this
24154          * @param {Number} index The index of the target node
24155          * @param {HTMLElement} node The target node
24156          * @param {Roo.EventObject} e The raw event object
24157          */
24158             "click" : true,
24159         /**
24160          * @event dblclick
24161          * Fires when a template node is double clicked.
24162          * @param {Roo.View} this
24163          * @param {Number} index The index of the target node
24164          * @param {HTMLElement} node The target node
24165          * @param {Roo.EventObject} e The raw event object
24166          */
24167             "dblclick" : true,
24168         /**
24169          * @event contextmenu
24170          * Fires when a template node is right clicked.
24171          * @param {Roo.View} this
24172          * @param {Number} index The index of the target node
24173          * @param {HTMLElement} node The target node
24174          * @param {Roo.EventObject} e The raw event object
24175          */
24176             "contextmenu" : true,
24177         /**
24178          * @event selectionchange
24179          * Fires when the selected nodes change.
24180          * @param {Roo.View} this
24181          * @param {Array} selections Array of the selected nodes
24182          */
24183             "selectionchange" : true,
24184     
24185         /**
24186          * @event beforeselect
24187          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24188          * @param {Roo.View} this
24189          * @param {HTMLElement} node The node to be selected
24190          * @param {Array} selections Array of currently selected nodes
24191          */
24192             "beforeselect" : true,
24193         /**
24194          * @event preparedata
24195          * Fires on every row to render, to allow you to change the data.
24196          * @param {Roo.View} this
24197          * @param {Object} data to be rendered (change this)
24198          */
24199           "preparedata" : true
24200           
24201           
24202         });
24203
24204
24205
24206     this.el.on({
24207         "click": this.onClick,
24208         "dblclick": this.onDblClick,
24209         "contextmenu": this.onContextMenu,
24210         scope:this
24211     });
24212
24213     this.selections = [];
24214     this.nodes = [];
24215     this.cmp = new Roo.CompositeElementLite([]);
24216     if(this.store){
24217         this.store = Roo.factory(this.store, Roo.data);
24218         this.setStore(this.store, true);
24219     }
24220     
24221     if ( this.footer && this.footer.xtype) {
24222            
24223          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24224         
24225         this.footer.dataSource = this.store
24226         this.footer.container = fctr;
24227         this.footer = Roo.factory(this.footer, Roo);
24228         fctr.insertFirst(this.el);
24229         
24230         // this is a bit insane - as the paging toolbar seems to detach the el..
24231 //        dom.parentNode.parentNode.parentNode
24232          // they get detached?
24233     }
24234     
24235     
24236     Roo.View.superclass.constructor.call(this);
24237     
24238     
24239 };
24240
24241 Roo.extend(Roo.View, Roo.util.Observable, {
24242     
24243      /**
24244      * @cfg {Roo.data.Store} store Data store to load data from.
24245      */
24246     store : false,
24247     
24248     /**
24249      * @cfg {String|Roo.Element} el The container element.
24250      */
24251     el : '',
24252     
24253     /**
24254      * @cfg {String|Roo.Template} tpl The template used by this View 
24255      */
24256     tpl : false,
24257     /**
24258      * @cfg {String} dataName the named area of the template to use as the data area
24259      *                          Works with domtemplates roo-name="name"
24260      */
24261     dataName: false,
24262     /**
24263      * @cfg {String} selectedClass The css class to add to selected nodes
24264      */
24265     selectedClass : "x-view-selected",
24266      /**
24267      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24268      */
24269     emptyText : "",
24270     
24271     /**
24272      * @cfg {String} text to display on mask (default Loading)
24273      */
24274     mask : false,
24275     /**
24276      * @cfg {Boolean} multiSelect Allow multiple selection
24277      */
24278     multiSelect : false,
24279     /**
24280      * @cfg {Boolean} singleSelect Allow single selection
24281      */
24282     singleSelect:  false,
24283     
24284     /**
24285      * @cfg {Boolean} toggleSelect - selecting 
24286      */
24287     toggleSelect : false,
24288     
24289     /**
24290      * Returns the element this view is bound to.
24291      * @return {Roo.Element}
24292      */
24293     getEl : function(){
24294         return this.wrapEl;
24295     },
24296     
24297     
24298
24299     /**
24300      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24301      */
24302     refresh : function(){
24303         var t = this.tpl;
24304         
24305         // if we are using something like 'domtemplate', then
24306         // the what gets used is:
24307         // t.applySubtemplate(NAME, data, wrapping data..)
24308         // the outer template then get' applied with
24309         //     the store 'extra data'
24310         // and the body get's added to the
24311         //      roo-name="data" node?
24312         //      <span class='roo-tpl-{name}'></span> ?????
24313         
24314         
24315         
24316         this.clearSelections();
24317         this.el.update("");
24318         var html = [];
24319         var records = this.store.getRange();
24320         if(records.length < 1) {
24321             
24322             // is this valid??  = should it render a template??
24323             
24324             this.el.update(this.emptyText);
24325             return;
24326         }
24327         var el = this.el;
24328         if (this.dataName) {
24329             this.el.update(t.apply(this.store.meta)); //????
24330             el = this.el.child('.roo-tpl-' + this.dataName);
24331         }
24332         
24333         for(var i = 0, len = records.length; i < len; i++){
24334             var data = this.prepareData(records[i].data, i, records[i]);
24335             this.fireEvent("preparedata", this, data, i, records[i]);
24336             html[html.length] = Roo.util.Format.trim(
24337                 this.dataName ?
24338                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24339                     t.apply(data)
24340             );
24341         }
24342         
24343         
24344         
24345         el.update(html.join(""));
24346         this.nodes = el.dom.childNodes;
24347         this.updateIndexes(0);
24348     },
24349
24350     /**
24351      * Function to override to reformat the data that is sent to
24352      * the template for each node.
24353      * DEPRICATED - use the preparedata event handler.
24354      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24355      * a JSON object for an UpdateManager bound view).
24356      */
24357     prepareData : function(data, index, record)
24358     {
24359         this.fireEvent("preparedata", this, data, index, record);
24360         return data;
24361     },
24362
24363     onUpdate : function(ds, record){
24364         this.clearSelections();
24365         var index = this.store.indexOf(record);
24366         var n = this.nodes[index];
24367         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24368         n.parentNode.removeChild(n);
24369         this.updateIndexes(index, index);
24370     },
24371
24372     
24373     
24374 // --------- FIXME     
24375     onAdd : function(ds, records, index)
24376     {
24377         this.clearSelections();
24378         if(this.nodes.length == 0){
24379             this.refresh();
24380             return;
24381         }
24382         var n = this.nodes[index];
24383         for(var i = 0, len = records.length; i < len; i++){
24384             var d = this.prepareData(records[i].data, i, records[i]);
24385             if(n){
24386                 this.tpl.insertBefore(n, d);
24387             }else{
24388                 
24389                 this.tpl.append(this.el, d);
24390             }
24391         }
24392         this.updateIndexes(index);
24393     },
24394
24395     onRemove : function(ds, record, index){
24396         this.clearSelections();
24397         var el = this.dataName  ?
24398             this.el.child('.roo-tpl-' + this.dataName) :
24399             this.el; 
24400         el.dom.removeChild(this.nodes[index]);
24401         this.updateIndexes(index);
24402     },
24403
24404     /**
24405      * Refresh an individual node.
24406      * @param {Number} index
24407      */
24408     refreshNode : function(index){
24409         this.onUpdate(this.store, this.store.getAt(index));
24410     },
24411
24412     updateIndexes : function(startIndex, endIndex){
24413         var ns = this.nodes;
24414         startIndex = startIndex || 0;
24415         endIndex = endIndex || ns.length - 1;
24416         for(var i = startIndex; i <= endIndex; i++){
24417             ns[i].nodeIndex = i;
24418         }
24419     },
24420
24421     /**
24422      * Changes the data store this view uses and refresh the view.
24423      * @param {Store} store
24424      */
24425     setStore : function(store, initial){
24426         if(!initial && this.store){
24427             this.store.un("datachanged", this.refresh);
24428             this.store.un("add", this.onAdd);
24429             this.store.un("remove", this.onRemove);
24430             this.store.un("update", this.onUpdate);
24431             this.store.un("clear", this.refresh);
24432             this.store.un("beforeload", this.onBeforeLoad);
24433             this.store.un("load", this.onLoad);
24434             this.store.un("loadexception", this.onLoad);
24435         }
24436         if(store){
24437           
24438             store.on("datachanged", this.refresh, this);
24439             store.on("add", this.onAdd, this);
24440             store.on("remove", this.onRemove, this);
24441             store.on("update", this.onUpdate, this);
24442             store.on("clear", this.refresh, this);
24443             store.on("beforeload", this.onBeforeLoad, this);
24444             store.on("load", this.onLoad, this);
24445             store.on("loadexception", this.onLoad, this);
24446         }
24447         
24448         if(store){
24449             this.refresh();
24450         }
24451     },
24452     /**
24453      * onbeforeLoad - masks the loading area.
24454      *
24455      */
24456     onBeforeLoad : function()
24457     {
24458         this.el.update("");
24459         this.el.mask(this.mask ? this.mask : "Loading" ); 
24460     },
24461     onLoad : function ()
24462     {
24463         this.el.unmask();
24464     },
24465     
24466
24467     /**
24468      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
24469      * @param {HTMLElement} node
24470      * @return {HTMLElement} The template node
24471      */
24472     findItemFromChild : function(node){
24473         var el = this.dataName  ?
24474             this.el.child('.roo-tpl-' + this.dataName,true) :
24475             this.el.dom; 
24476         
24477         if(!node || node.parentNode == el){
24478                     return node;
24479             }
24480             var p = node.parentNode;
24481             while(p && p != el){
24482             if(p.parentNode == el){
24483                 return p;
24484             }
24485             p = p.parentNode;
24486         }
24487             return null;
24488     },
24489
24490     /** @ignore */
24491     onClick : function(e){
24492         var item = this.findItemFromChild(e.getTarget());
24493         if(item){
24494             var index = this.indexOf(item);
24495             if(this.onItemClick(item, index, e) !== false){
24496                 this.fireEvent("click", this, index, item, e);
24497             }
24498         }else{
24499             this.clearSelections();
24500         }
24501     },
24502
24503     /** @ignore */
24504     onContextMenu : function(e){
24505         var item = this.findItemFromChild(e.getTarget());
24506         if(item){
24507             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
24508         }
24509     },
24510
24511     /** @ignore */
24512     onDblClick : function(e){
24513         var item = this.findItemFromChild(e.getTarget());
24514         if(item){
24515             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
24516         }
24517     },
24518
24519     onItemClick : function(item, index, e)
24520     {
24521         if(this.fireEvent("beforeclick", this, index, item, e) === false){
24522             return false;
24523         }
24524         if (this.toggleSelect) {
24525             var m = this.isSelected(item) ? 'unselect' : 'select';
24526             Roo.log(m);
24527             var _t = this;
24528             _t[m](item, true, false);
24529             return true;
24530         }
24531         if(this.multiSelect || this.singleSelect){
24532             if(this.multiSelect && e.shiftKey && this.lastSelection){
24533                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
24534             }else{
24535                 this.select(item, this.multiSelect && e.ctrlKey);
24536                 this.lastSelection = item;
24537             }
24538             e.preventDefault();
24539         }
24540         return true;
24541     },
24542
24543     /**
24544      * Get the number of selected nodes.
24545      * @return {Number}
24546      */
24547     getSelectionCount : function(){
24548         return this.selections.length;
24549     },
24550
24551     /**
24552      * Get the currently selected nodes.
24553      * @return {Array} An array of HTMLElements
24554      */
24555     getSelectedNodes : function(){
24556         return this.selections;
24557     },
24558
24559     /**
24560      * Get the indexes of the selected nodes.
24561      * @return {Array}
24562      */
24563     getSelectedIndexes : function(){
24564         var indexes = [], s = this.selections;
24565         for(var i = 0, len = s.length; i < len; i++){
24566             indexes.push(s[i].nodeIndex);
24567         }
24568         return indexes;
24569     },
24570
24571     /**
24572      * Clear all selections
24573      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
24574      */
24575     clearSelections : function(suppressEvent){
24576         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24577             this.cmp.elements = this.selections;
24578             this.cmp.removeClass(this.selectedClass);
24579             this.selections = [];
24580             if(!suppressEvent){
24581                 this.fireEvent("selectionchange", this, this.selections);
24582             }
24583         }
24584     },
24585
24586     /**
24587      * Returns true if the passed node is selected
24588      * @param {HTMLElement/Number} node The node or node index
24589      * @return {Boolean}
24590      */
24591     isSelected : function(node){
24592         var s = this.selections;
24593         if(s.length < 1){
24594             return false;
24595         }
24596         node = this.getNode(node);
24597         return s.indexOf(node) !== -1;
24598     },
24599
24600     /**
24601      * Selects nodes.
24602      * @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
24603      * @param {Boolean} keepExisting (optional) true to keep existing selections
24604      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24605      */
24606     select : function(nodeInfo, keepExisting, suppressEvent){
24607         if(nodeInfo instanceof Array){
24608             if(!keepExisting){
24609                 this.clearSelections(true);
24610             }
24611             for(var i = 0, len = nodeInfo.length; i < len; i++){
24612                 this.select(nodeInfo[i], true, true);
24613             }
24614             return;
24615         } 
24616         var node = this.getNode(nodeInfo);
24617         if(!node || this.isSelected(node)){
24618             return; // already selected.
24619         }
24620         if(!keepExisting){
24621             this.clearSelections(true);
24622         }
24623         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24624             Roo.fly(node).addClass(this.selectedClass);
24625             this.selections.push(node);
24626             if(!suppressEvent){
24627                 this.fireEvent("selectionchange", this, this.selections);
24628             }
24629         }
24630         
24631         
24632     },
24633       /**
24634      * Unselects nodes.
24635      * @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
24636      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24637      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24638      */
24639     unselect : function(nodeInfo, keepExisting, suppressEvent)
24640     {
24641         if(nodeInfo instanceof Array){
24642             Roo.each(this.selections, function(s) {
24643                 this.unselect(s, nodeInfo);
24644             }, this);
24645             return;
24646         }
24647         var node = this.getNode(nodeInfo);
24648         if(!node || !this.isSelected(node)){
24649             Roo.log("not selected");
24650             return; // not selected.
24651         }
24652         // fireevent???
24653         var ns = [];
24654         Roo.each(this.selections, function(s) {
24655             if (s == node ) {
24656                 Roo.fly(node).removeClass(this.selectedClass);
24657
24658                 return;
24659             }
24660             ns.push(s);
24661         },this);
24662         
24663         this.selections= ns;
24664         this.fireEvent("selectionchange", this, this.selections);
24665     },
24666
24667     /**
24668      * Gets a template node.
24669      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24670      * @return {HTMLElement} The node or null if it wasn't found
24671      */
24672     getNode : function(nodeInfo){
24673         if(typeof nodeInfo == "string"){
24674             return document.getElementById(nodeInfo);
24675         }else if(typeof nodeInfo == "number"){
24676             return this.nodes[nodeInfo];
24677         }
24678         return nodeInfo;
24679     },
24680
24681     /**
24682      * Gets a range template nodes.
24683      * @param {Number} startIndex
24684      * @param {Number} endIndex
24685      * @return {Array} An array of nodes
24686      */
24687     getNodes : function(start, end){
24688         var ns = this.nodes;
24689         start = start || 0;
24690         end = typeof end == "undefined" ? ns.length - 1 : end;
24691         var nodes = [];
24692         if(start <= end){
24693             for(var i = start; i <= end; i++){
24694                 nodes.push(ns[i]);
24695             }
24696         } else{
24697             for(var i = start; i >= end; i--){
24698                 nodes.push(ns[i]);
24699             }
24700         }
24701         return nodes;
24702     },
24703
24704     /**
24705      * Finds the index of the passed node
24706      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24707      * @return {Number} The index of the node or -1
24708      */
24709     indexOf : function(node){
24710         node = this.getNode(node);
24711         if(typeof node.nodeIndex == "number"){
24712             return node.nodeIndex;
24713         }
24714         var ns = this.nodes;
24715         for(var i = 0, len = ns.length; i < len; i++){
24716             if(ns[i] == node){
24717                 return i;
24718             }
24719         }
24720         return -1;
24721     }
24722 });
24723 /*
24724  * Based on:
24725  * Ext JS Library 1.1.1
24726  * Copyright(c) 2006-2007, Ext JS, LLC.
24727  *
24728  * Originally Released Under LGPL - original licence link has changed is not relivant.
24729  *
24730  * Fork - LGPL
24731  * <script type="text/javascript">
24732  */
24733
24734 /**
24735  * @class Roo.JsonView
24736  * @extends Roo.View
24737  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24738 <pre><code>
24739 var view = new Roo.JsonView({
24740     container: "my-element",
24741     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24742     multiSelect: true, 
24743     jsonRoot: "data" 
24744 });
24745
24746 // listen for node click?
24747 view.on("click", function(vw, index, node, e){
24748     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24749 });
24750
24751 // direct load of JSON data
24752 view.load("foobar.php");
24753
24754 // Example from my blog list
24755 var tpl = new Roo.Template(
24756     '&lt;div class="entry"&gt;' +
24757     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24758     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24759     "&lt;/div&gt;&lt;hr /&gt;"
24760 );
24761
24762 var moreView = new Roo.JsonView({
24763     container :  "entry-list", 
24764     template : tpl,
24765     jsonRoot: "posts"
24766 });
24767 moreView.on("beforerender", this.sortEntries, this);
24768 moreView.load({
24769     url: "/blog/get-posts.php",
24770     params: "allposts=true",
24771     text: "Loading Blog Entries..."
24772 });
24773 </code></pre>
24774
24775 * Note: old code is supported with arguments : (container, template, config)
24776
24777
24778  * @constructor
24779  * Create a new JsonView
24780  * 
24781  * @param {Object} config The config object
24782  * 
24783  */
24784 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24785     
24786     
24787     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24788
24789     var um = this.el.getUpdateManager();
24790     um.setRenderer(this);
24791     um.on("update", this.onLoad, this);
24792     um.on("failure", this.onLoadException, this);
24793
24794     /**
24795      * @event beforerender
24796      * Fires before rendering of the downloaded JSON data.
24797      * @param {Roo.JsonView} this
24798      * @param {Object} data The JSON data loaded
24799      */
24800     /**
24801      * @event load
24802      * Fires when data is loaded.
24803      * @param {Roo.JsonView} this
24804      * @param {Object} data The JSON data loaded
24805      * @param {Object} response The raw Connect response object
24806      */
24807     /**
24808      * @event loadexception
24809      * Fires when loading fails.
24810      * @param {Roo.JsonView} this
24811      * @param {Object} response The raw Connect response object
24812      */
24813     this.addEvents({
24814         'beforerender' : true,
24815         'load' : true,
24816         'loadexception' : true
24817     });
24818 };
24819 Roo.extend(Roo.JsonView, Roo.View, {
24820     /**
24821      * @type {String} The root property in the loaded JSON object that contains the data
24822      */
24823     jsonRoot : "",
24824
24825     /**
24826      * Refreshes the view.
24827      */
24828     refresh : function(){
24829         this.clearSelections();
24830         this.el.update("");
24831         var html = [];
24832         var o = this.jsonData;
24833         if(o && o.length > 0){
24834             for(var i = 0, len = o.length; i < len; i++){
24835                 var data = this.prepareData(o[i], i, o);
24836                 html[html.length] = this.tpl.apply(data);
24837             }
24838         }else{
24839             html.push(this.emptyText);
24840         }
24841         this.el.update(html.join(""));
24842         this.nodes = this.el.dom.childNodes;
24843         this.updateIndexes(0);
24844     },
24845
24846     /**
24847      * 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.
24848      * @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:
24849      <pre><code>
24850      view.load({
24851          url: "your-url.php",
24852          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24853          callback: yourFunction,
24854          scope: yourObject, //(optional scope)
24855          discardUrl: false,
24856          nocache: false,
24857          text: "Loading...",
24858          timeout: 30,
24859          scripts: false
24860      });
24861      </code></pre>
24862      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24863      * 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.
24864      * @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}
24865      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24866      * @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.
24867      */
24868     load : function(){
24869         var um = this.el.getUpdateManager();
24870         um.update.apply(um, arguments);
24871     },
24872
24873     render : function(el, response){
24874         this.clearSelections();
24875         this.el.update("");
24876         var o;
24877         try{
24878             o = Roo.util.JSON.decode(response.responseText);
24879             if(this.jsonRoot){
24880                 
24881                 o = o[this.jsonRoot];
24882             }
24883         } catch(e){
24884         }
24885         /**
24886          * The current JSON data or null
24887          */
24888         this.jsonData = o;
24889         this.beforeRender();
24890         this.refresh();
24891     },
24892
24893 /**
24894  * Get the number of records in the current JSON dataset
24895  * @return {Number}
24896  */
24897     getCount : function(){
24898         return this.jsonData ? this.jsonData.length : 0;
24899     },
24900
24901 /**
24902  * Returns the JSON object for the specified node(s)
24903  * @param {HTMLElement/Array} node The node or an array of nodes
24904  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24905  * you get the JSON object for the node
24906  */
24907     getNodeData : function(node){
24908         if(node instanceof Array){
24909             var data = [];
24910             for(var i = 0, len = node.length; i < len; i++){
24911                 data.push(this.getNodeData(node[i]));
24912             }
24913             return data;
24914         }
24915         return this.jsonData[this.indexOf(node)] || null;
24916     },
24917
24918     beforeRender : function(){
24919         this.snapshot = this.jsonData;
24920         if(this.sortInfo){
24921             this.sort.apply(this, this.sortInfo);
24922         }
24923         this.fireEvent("beforerender", this, this.jsonData);
24924     },
24925
24926     onLoad : function(el, o){
24927         this.fireEvent("load", this, this.jsonData, o);
24928     },
24929
24930     onLoadException : function(el, o){
24931         this.fireEvent("loadexception", this, o);
24932     },
24933
24934 /**
24935  * Filter the data by a specific property.
24936  * @param {String} property A property on your JSON objects
24937  * @param {String/RegExp} value Either string that the property values
24938  * should start with, or a RegExp to test against the property
24939  */
24940     filter : function(property, value){
24941         if(this.jsonData){
24942             var data = [];
24943             var ss = this.snapshot;
24944             if(typeof value == "string"){
24945                 var vlen = value.length;
24946                 if(vlen == 0){
24947                     this.clearFilter();
24948                     return;
24949                 }
24950                 value = value.toLowerCase();
24951                 for(var i = 0, len = ss.length; i < len; i++){
24952                     var o = ss[i];
24953                     if(o[property].substr(0, vlen).toLowerCase() == value){
24954                         data.push(o);
24955                     }
24956                 }
24957             } else if(value.exec){ // regex?
24958                 for(var i = 0, len = ss.length; i < len; i++){
24959                     var o = ss[i];
24960                     if(value.test(o[property])){
24961                         data.push(o);
24962                     }
24963                 }
24964             } else{
24965                 return;
24966             }
24967             this.jsonData = data;
24968             this.refresh();
24969         }
24970     },
24971
24972 /**
24973  * Filter by a function. The passed function will be called with each
24974  * object in the current dataset. If the function returns true the value is kept,
24975  * otherwise it is filtered.
24976  * @param {Function} fn
24977  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24978  */
24979     filterBy : function(fn, scope){
24980         if(this.jsonData){
24981             var data = [];
24982             var ss = this.snapshot;
24983             for(var i = 0, len = ss.length; i < len; i++){
24984                 var o = ss[i];
24985                 if(fn.call(scope || this, o)){
24986                     data.push(o);
24987                 }
24988             }
24989             this.jsonData = data;
24990             this.refresh();
24991         }
24992     },
24993
24994 /**
24995  * Clears the current filter.
24996  */
24997     clearFilter : function(){
24998         if(this.snapshot && this.jsonData != this.snapshot){
24999             this.jsonData = this.snapshot;
25000             this.refresh();
25001         }
25002     },
25003
25004
25005 /**
25006  * Sorts the data for this view and refreshes it.
25007  * @param {String} property A property on your JSON objects to sort on
25008  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25009  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25010  */
25011     sort : function(property, dir, sortType){
25012         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25013         if(this.jsonData){
25014             var p = property;
25015             var dsc = dir && dir.toLowerCase() == "desc";
25016             var f = function(o1, o2){
25017                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25018                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25019                 ;
25020                 if(v1 < v2){
25021                     return dsc ? +1 : -1;
25022                 } else if(v1 > v2){
25023                     return dsc ? -1 : +1;
25024                 } else{
25025                     return 0;
25026                 }
25027             };
25028             this.jsonData.sort(f);
25029             this.refresh();
25030             if(this.jsonData != this.snapshot){
25031                 this.snapshot.sort(f);
25032             }
25033         }
25034     }
25035 });/*
25036  * Based on:
25037  * Ext JS Library 1.1.1
25038  * Copyright(c) 2006-2007, Ext JS, LLC.
25039  *
25040  * Originally Released Under LGPL - original licence link has changed is not relivant.
25041  *
25042  * Fork - LGPL
25043  * <script type="text/javascript">
25044  */
25045  
25046
25047 /**
25048  * @class Roo.ColorPalette
25049  * @extends Roo.Component
25050  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25051  * Here's an example of typical usage:
25052  * <pre><code>
25053 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25054 cp.render('my-div');
25055
25056 cp.on('select', function(palette, selColor){
25057     // do something with selColor
25058 });
25059 </code></pre>
25060  * @constructor
25061  * Create a new ColorPalette
25062  * @param {Object} config The config object
25063  */
25064 Roo.ColorPalette = function(config){
25065     Roo.ColorPalette.superclass.constructor.call(this, config);
25066     this.addEvents({
25067         /**
25068              * @event select
25069              * Fires when a color is selected
25070              * @param {ColorPalette} this
25071              * @param {String} color The 6-digit color hex code (without the # symbol)
25072              */
25073         select: true
25074     });
25075
25076     if(this.handler){
25077         this.on("select", this.handler, this.scope, true);
25078     }
25079 };
25080 Roo.extend(Roo.ColorPalette, Roo.Component, {
25081     /**
25082      * @cfg {String} itemCls
25083      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25084      */
25085     itemCls : "x-color-palette",
25086     /**
25087      * @cfg {String} value
25088      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25089      * the hex codes are case-sensitive.
25090      */
25091     value : null,
25092     clickEvent:'click',
25093     // private
25094     ctype: "Roo.ColorPalette",
25095
25096     /**
25097      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25098      */
25099     allowReselect : false,
25100
25101     /**
25102      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25103      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25104      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25105      * of colors with the width setting until the box is symmetrical.</p>
25106      * <p>You can override individual colors if needed:</p>
25107      * <pre><code>
25108 var cp = new Roo.ColorPalette();
25109 cp.colors[0] = "FF0000";  // change the first box to red
25110 </code></pre>
25111
25112 Or you can provide a custom array of your own for complete control:
25113 <pre><code>
25114 var cp = new Roo.ColorPalette();
25115 cp.colors = ["000000", "993300", "333300"];
25116 </code></pre>
25117      * @type Array
25118      */
25119     colors : [
25120         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25121         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25122         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25123         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25124         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25125     ],
25126
25127     // private
25128     onRender : function(container, position){
25129         var t = new Roo.MasterTemplate(
25130             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25131         );
25132         var c = this.colors;
25133         for(var i = 0, len = c.length; i < len; i++){
25134             t.add([c[i]]);
25135         }
25136         var el = document.createElement("div");
25137         el.className = this.itemCls;
25138         t.overwrite(el);
25139         container.dom.insertBefore(el, position);
25140         this.el = Roo.get(el);
25141         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25142         if(this.clickEvent != 'click'){
25143             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25144         }
25145     },
25146
25147     // private
25148     afterRender : function(){
25149         Roo.ColorPalette.superclass.afterRender.call(this);
25150         if(this.value){
25151             var s = this.value;
25152             this.value = null;
25153             this.select(s);
25154         }
25155     },
25156
25157     // private
25158     handleClick : function(e, t){
25159         e.preventDefault();
25160         if(!this.disabled){
25161             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25162             this.select(c.toUpperCase());
25163         }
25164     },
25165
25166     /**
25167      * Selects the specified color in the palette (fires the select event)
25168      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25169      */
25170     select : function(color){
25171         color = color.replace("#", "");
25172         if(color != this.value || this.allowReselect){
25173             var el = this.el;
25174             if(this.value){
25175                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25176             }
25177             el.child("a.color-"+color).addClass("x-color-palette-sel");
25178             this.value = color;
25179             this.fireEvent("select", this, color);
25180         }
25181     }
25182 });/*
25183  * Based on:
25184  * Ext JS Library 1.1.1
25185  * Copyright(c) 2006-2007, Ext JS, LLC.
25186  *
25187  * Originally Released Under LGPL - original licence link has changed is not relivant.
25188  *
25189  * Fork - LGPL
25190  * <script type="text/javascript">
25191  */
25192  
25193 /**
25194  * @class Roo.DatePicker
25195  * @extends Roo.Component
25196  * Simple date picker class.
25197  * @constructor
25198  * Create a new DatePicker
25199  * @param {Object} config The config object
25200  */
25201 Roo.DatePicker = function(config){
25202     Roo.DatePicker.superclass.constructor.call(this, config);
25203
25204     this.value = config && config.value ?
25205                  config.value.clearTime() : new Date().clearTime();
25206
25207     this.addEvents({
25208         /**
25209              * @event select
25210              * Fires when a date is selected
25211              * @param {DatePicker} this
25212              * @param {Date} date The selected date
25213              */
25214         'select': true,
25215         /**
25216              * @event monthchange
25217              * Fires when the displayed month changes 
25218              * @param {DatePicker} this
25219              * @param {Date} date The selected month
25220              */
25221         'monthchange': true
25222     });
25223
25224     if(this.handler){
25225         this.on("select", this.handler,  this.scope || this);
25226     }
25227     // build the disabledDatesRE
25228     if(!this.disabledDatesRE && this.disabledDates){
25229         var dd = this.disabledDates;
25230         var re = "(?:";
25231         for(var i = 0; i < dd.length; i++){
25232             re += dd[i];
25233             if(i != dd.length-1) re += "|";
25234         }
25235         this.disabledDatesRE = new RegExp(re + ")");
25236     }
25237 };
25238
25239 Roo.extend(Roo.DatePicker, Roo.Component, {
25240     /**
25241      * @cfg {String} todayText
25242      * The text to display on the button that selects the current date (defaults to "Today")
25243      */
25244     todayText : "Today",
25245     /**
25246      * @cfg {String} okText
25247      * The text to display on the ok button
25248      */
25249     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25250     /**
25251      * @cfg {String} cancelText
25252      * The text to display on the cancel button
25253      */
25254     cancelText : "Cancel",
25255     /**
25256      * @cfg {String} todayTip
25257      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25258      */
25259     todayTip : "{0} (Spacebar)",
25260     /**
25261      * @cfg {Date} minDate
25262      * Minimum allowable date (JavaScript date object, defaults to null)
25263      */
25264     minDate : null,
25265     /**
25266      * @cfg {Date} maxDate
25267      * Maximum allowable date (JavaScript date object, defaults to null)
25268      */
25269     maxDate : null,
25270     /**
25271      * @cfg {String} minText
25272      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25273      */
25274     minText : "This date is before the minimum date",
25275     /**
25276      * @cfg {String} maxText
25277      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25278      */
25279     maxText : "This date is after the maximum date",
25280     /**
25281      * @cfg {String} format
25282      * The default date format string which can be overriden for localization support.  The format must be
25283      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25284      */
25285     format : "m/d/y",
25286     /**
25287      * @cfg {Array} disabledDays
25288      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25289      */
25290     disabledDays : null,
25291     /**
25292      * @cfg {String} disabledDaysText
25293      * The tooltip to display when the date falls on a disabled day (defaults to "")
25294      */
25295     disabledDaysText : "",
25296     /**
25297      * @cfg {RegExp} disabledDatesRE
25298      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25299      */
25300     disabledDatesRE : null,
25301     /**
25302      * @cfg {String} disabledDatesText
25303      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25304      */
25305     disabledDatesText : "",
25306     /**
25307      * @cfg {Boolean} constrainToViewport
25308      * True to constrain the date picker to the viewport (defaults to true)
25309      */
25310     constrainToViewport : true,
25311     /**
25312      * @cfg {Array} monthNames
25313      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25314      */
25315     monthNames : Date.monthNames,
25316     /**
25317      * @cfg {Array} dayNames
25318      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25319      */
25320     dayNames : Date.dayNames,
25321     /**
25322      * @cfg {String} nextText
25323      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25324      */
25325     nextText: 'Next Month (Control+Right)',
25326     /**
25327      * @cfg {String} prevText
25328      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25329      */
25330     prevText: 'Previous Month (Control+Left)',
25331     /**
25332      * @cfg {String} monthYearText
25333      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25334      */
25335     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25336     /**
25337      * @cfg {Number} startDay
25338      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25339      */
25340     startDay : 0,
25341     /**
25342      * @cfg {Bool} showClear
25343      * Show a clear button (usefull for date form elements that can be blank.)
25344      */
25345     
25346     showClear: false,
25347     
25348     /**
25349      * Sets the value of the date field
25350      * @param {Date} value The date to set
25351      */
25352     setValue : function(value){
25353         var old = this.value;
25354         
25355         if (typeof(value) == 'string') {
25356          
25357             value = Date.parseDate(value, this.format);
25358         }
25359         if (!value) {
25360             value = new Date();
25361         }
25362         
25363         this.value = value.clearTime(true);
25364         if(this.el){
25365             this.update(this.value);
25366         }
25367     },
25368
25369     /**
25370      * Gets the current selected value of the date field
25371      * @return {Date} The selected date
25372      */
25373     getValue : function(){
25374         return this.value;
25375     },
25376
25377     // private
25378     focus : function(){
25379         if(this.el){
25380             this.update(this.activeDate);
25381         }
25382     },
25383
25384     // privateval
25385     onRender : function(container, position){
25386         
25387         var m = [
25388              '<table cellspacing="0">',
25389                 '<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>',
25390                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
25391         var dn = this.dayNames;
25392         for(var i = 0; i < 7; i++){
25393             var d = this.startDay+i;
25394             if(d > 6){
25395                 d = d-7;
25396             }
25397             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
25398         }
25399         m[m.length] = "</tr></thead><tbody><tr>";
25400         for(var i = 0; i < 42; i++) {
25401             if(i % 7 == 0 && i != 0){
25402                 m[m.length] = "</tr><tr>";
25403             }
25404             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
25405         }
25406         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
25407             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
25408
25409         var el = document.createElement("div");
25410         el.className = "x-date-picker";
25411         el.innerHTML = m.join("");
25412
25413         container.dom.insertBefore(el, position);
25414
25415         this.el = Roo.get(el);
25416         this.eventEl = Roo.get(el.firstChild);
25417
25418         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
25419             handler: this.showPrevMonth,
25420             scope: this,
25421             preventDefault:true,
25422             stopDefault:true
25423         });
25424
25425         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
25426             handler: this.showNextMonth,
25427             scope: this,
25428             preventDefault:true,
25429             stopDefault:true
25430         });
25431
25432         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
25433
25434         this.monthPicker = this.el.down('div.x-date-mp');
25435         this.monthPicker.enableDisplayMode('block');
25436         
25437         var kn = new Roo.KeyNav(this.eventEl, {
25438             "left" : function(e){
25439                 e.ctrlKey ?
25440                     this.showPrevMonth() :
25441                     this.update(this.activeDate.add("d", -1));
25442             },
25443
25444             "right" : function(e){
25445                 e.ctrlKey ?
25446                     this.showNextMonth() :
25447                     this.update(this.activeDate.add("d", 1));
25448             },
25449
25450             "up" : function(e){
25451                 e.ctrlKey ?
25452                     this.showNextYear() :
25453                     this.update(this.activeDate.add("d", -7));
25454             },
25455
25456             "down" : function(e){
25457                 e.ctrlKey ?
25458                     this.showPrevYear() :
25459                     this.update(this.activeDate.add("d", 7));
25460             },
25461
25462             "pageUp" : function(e){
25463                 this.showNextMonth();
25464             },
25465
25466             "pageDown" : function(e){
25467                 this.showPrevMonth();
25468             },
25469
25470             "enter" : function(e){
25471                 e.stopPropagation();
25472                 return true;
25473             },
25474
25475             scope : this
25476         });
25477
25478         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
25479
25480         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
25481
25482         this.el.unselectable();
25483         
25484         this.cells = this.el.select("table.x-date-inner tbody td");
25485         this.textNodes = this.el.query("table.x-date-inner tbody span");
25486
25487         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
25488             text: "&#160;",
25489             tooltip: this.monthYearText
25490         });
25491
25492         this.mbtn.on('click', this.showMonthPicker, this);
25493         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
25494
25495
25496         var today = (new Date()).dateFormat(this.format);
25497         
25498         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
25499         if (this.showClear) {
25500             baseTb.add( new Roo.Toolbar.Fill());
25501         }
25502         baseTb.add({
25503             text: String.format(this.todayText, today),
25504             tooltip: String.format(this.todayTip, today),
25505             handler: this.selectToday,
25506             scope: this
25507         });
25508         
25509         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
25510             
25511         //});
25512         if (this.showClear) {
25513             
25514             baseTb.add( new Roo.Toolbar.Fill());
25515             baseTb.add({
25516                 text: '&#160;',
25517                 cls: 'x-btn-icon x-btn-clear',
25518                 handler: function() {
25519                     //this.value = '';
25520                     this.fireEvent("select", this, '');
25521                 },
25522                 scope: this
25523             });
25524         }
25525         
25526         
25527         if(Roo.isIE){
25528             this.el.repaint();
25529         }
25530         this.update(this.value);
25531     },
25532
25533     createMonthPicker : function(){
25534         if(!this.monthPicker.dom.firstChild){
25535             var buf = ['<table border="0" cellspacing="0">'];
25536             for(var i = 0; i < 6; i++){
25537                 buf.push(
25538                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
25539                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
25540                     i == 0 ?
25541                     '<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>' :
25542                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
25543                 );
25544             }
25545             buf.push(
25546                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
25547                     this.okText,
25548                     '</button><button type="button" class="x-date-mp-cancel">',
25549                     this.cancelText,
25550                     '</button></td></tr>',
25551                 '</table>'
25552             );
25553             this.monthPicker.update(buf.join(''));
25554             this.monthPicker.on('click', this.onMonthClick, this);
25555             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
25556
25557             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
25558             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
25559
25560             this.mpMonths.each(function(m, a, i){
25561                 i += 1;
25562                 if((i%2) == 0){
25563                     m.dom.xmonth = 5 + Math.round(i * .5);
25564                 }else{
25565                     m.dom.xmonth = Math.round((i-1) * .5);
25566                 }
25567             });
25568         }
25569     },
25570
25571     showMonthPicker : function(){
25572         this.createMonthPicker();
25573         var size = this.el.getSize();
25574         this.monthPicker.setSize(size);
25575         this.monthPicker.child('table').setSize(size);
25576
25577         this.mpSelMonth = (this.activeDate || this.value).getMonth();
25578         this.updateMPMonth(this.mpSelMonth);
25579         this.mpSelYear = (this.activeDate || this.value).getFullYear();
25580         this.updateMPYear(this.mpSelYear);
25581
25582         this.monthPicker.slideIn('t', {duration:.2});
25583     },
25584
25585     updateMPYear : function(y){
25586         this.mpyear = y;
25587         var ys = this.mpYears.elements;
25588         for(var i = 1; i <= 10; i++){
25589             var td = ys[i-1], y2;
25590             if((i%2) == 0){
25591                 y2 = y + Math.round(i * .5);
25592                 td.firstChild.innerHTML = y2;
25593                 td.xyear = y2;
25594             }else{
25595                 y2 = y - (5-Math.round(i * .5));
25596                 td.firstChild.innerHTML = y2;
25597                 td.xyear = y2;
25598             }
25599             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25600         }
25601     },
25602
25603     updateMPMonth : function(sm){
25604         this.mpMonths.each(function(m, a, i){
25605             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25606         });
25607     },
25608
25609     selectMPMonth: function(m){
25610         
25611     },
25612
25613     onMonthClick : function(e, t){
25614         e.stopEvent();
25615         var el = new Roo.Element(t), pn;
25616         if(el.is('button.x-date-mp-cancel')){
25617             this.hideMonthPicker();
25618         }
25619         else if(el.is('button.x-date-mp-ok')){
25620             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25621             this.hideMonthPicker();
25622         }
25623         else if(pn = el.up('td.x-date-mp-month', 2)){
25624             this.mpMonths.removeClass('x-date-mp-sel');
25625             pn.addClass('x-date-mp-sel');
25626             this.mpSelMonth = pn.dom.xmonth;
25627         }
25628         else if(pn = el.up('td.x-date-mp-year', 2)){
25629             this.mpYears.removeClass('x-date-mp-sel');
25630             pn.addClass('x-date-mp-sel');
25631             this.mpSelYear = pn.dom.xyear;
25632         }
25633         else if(el.is('a.x-date-mp-prev')){
25634             this.updateMPYear(this.mpyear-10);
25635         }
25636         else if(el.is('a.x-date-mp-next')){
25637             this.updateMPYear(this.mpyear+10);
25638         }
25639     },
25640
25641     onMonthDblClick : function(e, t){
25642         e.stopEvent();
25643         var el = new Roo.Element(t), pn;
25644         if(pn = el.up('td.x-date-mp-month', 2)){
25645             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25646             this.hideMonthPicker();
25647         }
25648         else if(pn = el.up('td.x-date-mp-year', 2)){
25649             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25650             this.hideMonthPicker();
25651         }
25652     },
25653
25654     hideMonthPicker : function(disableAnim){
25655         if(this.monthPicker){
25656             if(disableAnim === true){
25657                 this.monthPicker.hide();
25658             }else{
25659                 this.monthPicker.slideOut('t', {duration:.2});
25660             }
25661         }
25662     },
25663
25664     // private
25665     showPrevMonth : function(e){
25666         this.update(this.activeDate.add("mo", -1));
25667     },
25668
25669     // private
25670     showNextMonth : function(e){
25671         this.update(this.activeDate.add("mo", 1));
25672     },
25673
25674     // private
25675     showPrevYear : function(){
25676         this.update(this.activeDate.add("y", -1));
25677     },
25678
25679     // private
25680     showNextYear : function(){
25681         this.update(this.activeDate.add("y", 1));
25682     },
25683
25684     // private
25685     handleMouseWheel : function(e){
25686         var delta = e.getWheelDelta();
25687         if(delta > 0){
25688             this.showPrevMonth();
25689             e.stopEvent();
25690         } else if(delta < 0){
25691             this.showNextMonth();
25692             e.stopEvent();
25693         }
25694     },
25695
25696     // private
25697     handleDateClick : function(e, t){
25698         e.stopEvent();
25699         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25700             this.setValue(new Date(t.dateValue));
25701             this.fireEvent("select", this, this.value);
25702         }
25703     },
25704
25705     // private
25706     selectToday : function(){
25707         this.setValue(new Date().clearTime());
25708         this.fireEvent("select", this, this.value);
25709     },
25710
25711     // private
25712     update : function(date)
25713     {
25714         var vd = this.activeDate;
25715         this.activeDate = date;
25716         if(vd && this.el){
25717             var t = date.getTime();
25718             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25719                 this.cells.removeClass("x-date-selected");
25720                 this.cells.each(function(c){
25721                    if(c.dom.firstChild.dateValue == t){
25722                        c.addClass("x-date-selected");
25723                        setTimeout(function(){
25724                             try{c.dom.firstChild.focus();}catch(e){}
25725                        }, 50);
25726                        return false;
25727                    }
25728                 });
25729                 return;
25730             }
25731         }
25732         
25733         var days = date.getDaysInMonth();
25734         var firstOfMonth = date.getFirstDateOfMonth();
25735         var startingPos = firstOfMonth.getDay()-this.startDay;
25736
25737         if(startingPos <= this.startDay){
25738             startingPos += 7;
25739         }
25740
25741         var pm = date.add("mo", -1);
25742         var prevStart = pm.getDaysInMonth()-startingPos;
25743
25744         var cells = this.cells.elements;
25745         var textEls = this.textNodes;
25746         days += startingPos;
25747
25748         // convert everything to numbers so it's fast
25749         var day = 86400000;
25750         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25751         var today = new Date().clearTime().getTime();
25752         var sel = date.clearTime().getTime();
25753         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25754         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25755         var ddMatch = this.disabledDatesRE;
25756         var ddText = this.disabledDatesText;
25757         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25758         var ddaysText = this.disabledDaysText;
25759         var format = this.format;
25760
25761         var setCellClass = function(cal, cell){
25762             cell.title = "";
25763             var t = d.getTime();
25764             cell.firstChild.dateValue = t;
25765             if(t == today){
25766                 cell.className += " x-date-today";
25767                 cell.title = cal.todayText;
25768             }
25769             if(t == sel){
25770                 cell.className += " x-date-selected";
25771                 setTimeout(function(){
25772                     try{cell.firstChild.focus();}catch(e){}
25773                 }, 50);
25774             }
25775             // disabling
25776             if(t < min) {
25777                 cell.className = " x-date-disabled";
25778                 cell.title = cal.minText;
25779                 return;
25780             }
25781             if(t > max) {
25782                 cell.className = " x-date-disabled";
25783                 cell.title = cal.maxText;
25784                 return;
25785             }
25786             if(ddays){
25787                 if(ddays.indexOf(d.getDay()) != -1){
25788                     cell.title = ddaysText;
25789                     cell.className = " x-date-disabled";
25790                 }
25791             }
25792             if(ddMatch && format){
25793                 var fvalue = d.dateFormat(format);
25794                 if(ddMatch.test(fvalue)){
25795                     cell.title = ddText.replace("%0", fvalue);
25796                     cell.className = " x-date-disabled";
25797                 }
25798             }
25799         };
25800
25801         var i = 0;
25802         for(; i < startingPos; i++) {
25803             textEls[i].innerHTML = (++prevStart);
25804             d.setDate(d.getDate()+1);
25805             cells[i].className = "x-date-prevday";
25806             setCellClass(this, cells[i]);
25807         }
25808         for(; i < days; i++){
25809             intDay = i - startingPos + 1;
25810             textEls[i].innerHTML = (intDay);
25811             d.setDate(d.getDate()+1);
25812             cells[i].className = "x-date-active";
25813             setCellClass(this, cells[i]);
25814         }
25815         var extraDays = 0;
25816         for(; i < 42; i++) {
25817              textEls[i].innerHTML = (++extraDays);
25818              d.setDate(d.getDate()+1);
25819              cells[i].className = "x-date-nextday";
25820              setCellClass(this, cells[i]);
25821         }
25822
25823         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25824         this.fireEvent('monthchange', this, date);
25825         
25826         if(!this.internalRender){
25827             var main = this.el.dom.firstChild;
25828             var w = main.offsetWidth;
25829             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25830             Roo.fly(main).setWidth(w);
25831             this.internalRender = true;
25832             // opera does not respect the auto grow header center column
25833             // then, after it gets a width opera refuses to recalculate
25834             // without a second pass
25835             if(Roo.isOpera && !this.secondPass){
25836                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25837                 this.secondPass = true;
25838                 this.update.defer(10, this, [date]);
25839             }
25840         }
25841         
25842         
25843     }
25844 });        /*
25845  * Based on:
25846  * Ext JS Library 1.1.1
25847  * Copyright(c) 2006-2007, Ext JS, LLC.
25848  *
25849  * Originally Released Under LGPL - original licence link has changed is not relivant.
25850  *
25851  * Fork - LGPL
25852  * <script type="text/javascript">
25853  */
25854 /**
25855  * @class Roo.TabPanel
25856  * @extends Roo.util.Observable
25857  * A lightweight tab container.
25858  * <br><br>
25859  * Usage:
25860  * <pre><code>
25861 // basic tabs 1, built from existing content
25862 var tabs = new Roo.TabPanel("tabs1");
25863 tabs.addTab("script", "View Script");
25864 tabs.addTab("markup", "View Markup");
25865 tabs.activate("script");
25866
25867 // more advanced tabs, built from javascript
25868 var jtabs = new Roo.TabPanel("jtabs");
25869 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25870
25871 // set up the UpdateManager
25872 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25873 var updater = tab2.getUpdateManager();
25874 updater.setDefaultUrl("ajax1.htm");
25875 tab2.on('activate', updater.refresh, updater, true);
25876
25877 // Use setUrl for Ajax loading
25878 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25879 tab3.setUrl("ajax2.htm", null, true);
25880
25881 // Disabled tab
25882 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25883 tab4.disable();
25884
25885 jtabs.activate("jtabs-1");
25886  * </code></pre>
25887  * @constructor
25888  * Create a new TabPanel.
25889  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25890  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25891  */
25892 Roo.TabPanel = function(container, config){
25893     /**
25894     * The container element for this TabPanel.
25895     * @type Roo.Element
25896     */
25897     this.el = Roo.get(container, true);
25898     if(config){
25899         if(typeof config == "boolean"){
25900             this.tabPosition = config ? "bottom" : "top";
25901         }else{
25902             Roo.apply(this, config);
25903         }
25904     }
25905     if(this.tabPosition == "bottom"){
25906         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25907         this.el.addClass("x-tabs-bottom");
25908     }
25909     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25910     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25911     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25912     if(Roo.isIE){
25913         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25914     }
25915     if(this.tabPosition != "bottom"){
25916         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25917          * @type Roo.Element
25918          */
25919         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25920         this.el.addClass("x-tabs-top");
25921     }
25922     this.items = [];
25923
25924     this.bodyEl.setStyle("position", "relative");
25925
25926     this.active = null;
25927     this.activateDelegate = this.activate.createDelegate(this);
25928
25929     this.addEvents({
25930         /**
25931          * @event tabchange
25932          * Fires when the active tab changes
25933          * @param {Roo.TabPanel} this
25934          * @param {Roo.TabPanelItem} activePanel The new active tab
25935          */
25936         "tabchange": true,
25937         /**
25938          * @event beforetabchange
25939          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25940          * @param {Roo.TabPanel} this
25941          * @param {Object} e Set cancel to true on this object to cancel the tab change
25942          * @param {Roo.TabPanelItem} tab The tab being changed to
25943          */
25944         "beforetabchange" : true
25945     });
25946
25947     Roo.EventManager.onWindowResize(this.onResize, this);
25948     this.cpad = this.el.getPadding("lr");
25949     this.hiddenCount = 0;
25950
25951
25952     // toolbar on the tabbar support...
25953     if (this.toolbar) {
25954         var tcfg = this.toolbar;
25955         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25956         this.toolbar = new Roo.Toolbar(tcfg);
25957         if (Roo.isSafari) {
25958             var tbl = tcfg.container.child('table', true);
25959             tbl.setAttribute('width', '100%');
25960         }
25961         
25962     }
25963    
25964
25965
25966     Roo.TabPanel.superclass.constructor.call(this);
25967 };
25968
25969 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25970     /*
25971      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25972      */
25973     tabPosition : "top",
25974     /*
25975      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25976      */
25977     currentTabWidth : 0,
25978     /*
25979      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25980      */
25981     minTabWidth : 40,
25982     /*
25983      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25984      */
25985     maxTabWidth : 250,
25986     /*
25987      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25988      */
25989     preferredTabWidth : 175,
25990     /*
25991      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25992      */
25993     resizeTabs : false,
25994     /*
25995      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25996      */
25997     monitorResize : true,
25998     /*
25999      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26000      */
26001     toolbar : false,
26002
26003     /**
26004      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26005      * @param {String} id The id of the div to use <b>or create</b>
26006      * @param {String} text The text for the tab
26007      * @param {String} content (optional) Content to put in the TabPanelItem body
26008      * @param {Boolean} closable (optional) True to create a close icon on the tab
26009      * @return {Roo.TabPanelItem} The created TabPanelItem
26010      */
26011     addTab : function(id, text, content, closable){
26012         var item = new Roo.TabPanelItem(this, id, text, closable);
26013         this.addTabItem(item);
26014         if(content){
26015             item.setContent(content);
26016         }
26017         return item;
26018     },
26019
26020     /**
26021      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26022      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26023      * @return {Roo.TabPanelItem}
26024      */
26025     getTab : function(id){
26026         return this.items[id];
26027     },
26028
26029     /**
26030      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26031      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26032      */
26033     hideTab : function(id){
26034         var t = this.items[id];
26035         if(!t.isHidden()){
26036            t.setHidden(true);
26037            this.hiddenCount++;
26038            this.autoSizeTabs();
26039         }
26040     },
26041
26042     /**
26043      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26044      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26045      */
26046     unhideTab : function(id){
26047         var t = this.items[id];
26048         if(t.isHidden()){
26049            t.setHidden(false);
26050            this.hiddenCount--;
26051            this.autoSizeTabs();
26052         }
26053     },
26054
26055     /**
26056      * Adds an existing {@link Roo.TabPanelItem}.
26057      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26058      */
26059     addTabItem : function(item){
26060         this.items[item.id] = item;
26061         this.items.push(item);
26062         if(this.resizeTabs){
26063            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26064            this.autoSizeTabs();
26065         }else{
26066             item.autoSize();
26067         }
26068     },
26069
26070     /**
26071      * Removes a {@link Roo.TabPanelItem}.
26072      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26073      */
26074     removeTab : function(id){
26075         var items = this.items;
26076         var tab = items[id];
26077         if(!tab) { return; }
26078         var index = items.indexOf(tab);
26079         if(this.active == tab && items.length > 1){
26080             var newTab = this.getNextAvailable(index);
26081             if(newTab) {
26082                 newTab.activate();
26083             }
26084         }
26085         this.stripEl.dom.removeChild(tab.pnode.dom);
26086         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26087             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26088         }
26089         items.splice(index, 1);
26090         delete this.items[tab.id];
26091         tab.fireEvent("close", tab);
26092         tab.purgeListeners();
26093         this.autoSizeTabs();
26094     },
26095
26096     getNextAvailable : function(start){
26097         var items = this.items;
26098         var index = start;
26099         // look for a next tab that will slide over to
26100         // replace the one being removed
26101         while(index < items.length){
26102             var item = items[++index];
26103             if(item && !item.isHidden()){
26104                 return item;
26105             }
26106         }
26107         // if one isn't found select the previous tab (on the left)
26108         index = start;
26109         while(index >= 0){
26110             var item = items[--index];
26111             if(item && !item.isHidden()){
26112                 return item;
26113             }
26114         }
26115         return null;
26116     },
26117
26118     /**
26119      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26120      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26121      */
26122     disableTab : function(id){
26123         var tab = this.items[id];
26124         if(tab && this.active != tab){
26125             tab.disable();
26126         }
26127     },
26128
26129     /**
26130      * Enables a {@link Roo.TabPanelItem} that is disabled.
26131      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26132      */
26133     enableTab : function(id){
26134         var tab = this.items[id];
26135         tab.enable();
26136     },
26137
26138     /**
26139      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26140      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26141      * @return {Roo.TabPanelItem} The TabPanelItem.
26142      */
26143     activate : function(id){
26144         var tab = this.items[id];
26145         if(!tab){
26146             return null;
26147         }
26148         if(tab == this.active || tab.disabled){
26149             return tab;
26150         }
26151         var e = {};
26152         this.fireEvent("beforetabchange", this, e, tab);
26153         if(e.cancel !== true && !tab.disabled){
26154             if(this.active){
26155                 this.active.hide();
26156             }
26157             this.active = this.items[id];
26158             this.active.show();
26159             this.fireEvent("tabchange", this, this.active);
26160         }
26161         return tab;
26162     },
26163
26164     /**
26165      * Gets the active {@link Roo.TabPanelItem}.
26166      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26167      */
26168     getActiveTab : function(){
26169         return this.active;
26170     },
26171
26172     /**
26173      * Updates the tab body element to fit the height of the container element
26174      * for overflow scrolling
26175      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26176      */
26177     syncHeight : function(targetHeight){
26178         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26179         var bm = this.bodyEl.getMargins();
26180         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26181         this.bodyEl.setHeight(newHeight);
26182         return newHeight;
26183     },
26184
26185     onResize : function(){
26186         if(this.monitorResize){
26187             this.autoSizeTabs();
26188         }
26189     },
26190
26191     /**
26192      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26193      */
26194     beginUpdate : function(){
26195         this.updating = true;
26196     },
26197
26198     /**
26199      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26200      */
26201     endUpdate : function(){
26202         this.updating = false;
26203         this.autoSizeTabs();
26204     },
26205
26206     /**
26207      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26208      */
26209     autoSizeTabs : function(){
26210         var count = this.items.length;
26211         var vcount = count - this.hiddenCount;
26212         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26213         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26214         var availWidth = Math.floor(w / vcount);
26215         var b = this.stripBody;
26216         if(b.getWidth() > w){
26217             var tabs = this.items;
26218             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26219             if(availWidth < this.minTabWidth){
26220                 /*if(!this.sleft){    // incomplete scrolling code
26221                     this.createScrollButtons();
26222                 }
26223                 this.showScroll();
26224                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26225             }
26226         }else{
26227             if(this.currentTabWidth < this.preferredTabWidth){
26228                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26229             }
26230         }
26231     },
26232
26233     /**
26234      * Returns the number of tabs in this TabPanel.
26235      * @return {Number}
26236      */
26237      getCount : function(){
26238          return this.items.length;
26239      },
26240
26241     /**
26242      * Resizes all the tabs to the passed width
26243      * @param {Number} The new width
26244      */
26245     setTabWidth : function(width){
26246         this.currentTabWidth = width;
26247         for(var i = 0, len = this.items.length; i < len; i++) {
26248                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26249         }
26250     },
26251
26252     /**
26253      * Destroys this TabPanel
26254      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26255      */
26256     destroy : function(removeEl){
26257         Roo.EventManager.removeResizeListener(this.onResize, this);
26258         for(var i = 0, len = this.items.length; i < len; i++){
26259             this.items[i].purgeListeners();
26260         }
26261         if(removeEl === true){
26262             this.el.update("");
26263             this.el.remove();
26264         }
26265     }
26266 });
26267
26268 /**
26269  * @class Roo.TabPanelItem
26270  * @extends Roo.util.Observable
26271  * Represents an individual item (tab plus body) in a TabPanel.
26272  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26273  * @param {String} id The id of this TabPanelItem
26274  * @param {String} text The text for the tab of this TabPanelItem
26275  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26276  */
26277 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26278     /**
26279      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26280      * @type Roo.TabPanel
26281      */
26282     this.tabPanel = tabPanel;
26283     /**
26284      * The id for this TabPanelItem
26285      * @type String
26286      */
26287     this.id = id;
26288     /** @private */
26289     this.disabled = false;
26290     /** @private */
26291     this.text = text;
26292     /** @private */
26293     this.loaded = false;
26294     this.closable = closable;
26295
26296     /**
26297      * The body element for this TabPanelItem.
26298      * @type Roo.Element
26299      */
26300     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26301     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26302     this.bodyEl.setStyle("display", "block");
26303     this.bodyEl.setStyle("zoom", "1");
26304     this.hideAction();
26305
26306     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26307     /** @private */
26308     this.el = Roo.get(els.el, true);
26309     this.inner = Roo.get(els.inner, true);
26310     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26311     this.pnode = Roo.get(els.el.parentNode, true);
26312     this.el.on("mousedown", this.onTabMouseDown, this);
26313     this.el.on("click", this.onTabClick, this);
26314     /** @private */
26315     if(closable){
26316         var c = Roo.get(els.close, true);
26317         c.dom.title = this.closeText;
26318         c.addClassOnOver("close-over");
26319         c.on("click", this.closeClick, this);
26320      }
26321
26322     this.addEvents({
26323          /**
26324          * @event activate
26325          * Fires when this tab becomes the active tab.
26326          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26327          * @param {Roo.TabPanelItem} this
26328          */
26329         "activate": true,
26330         /**
26331          * @event beforeclose
26332          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26333          * @param {Roo.TabPanelItem} this
26334          * @param {Object} e Set cancel to true on this object to cancel the close.
26335          */
26336         "beforeclose": true,
26337         /**
26338          * @event close
26339          * Fires when this tab is closed.
26340          * @param {Roo.TabPanelItem} this
26341          */
26342          "close": true,
26343         /**
26344          * @event deactivate
26345          * Fires when this tab is no longer the active tab.
26346          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26347          * @param {Roo.TabPanelItem} this
26348          */
26349          "deactivate" : true
26350     });
26351     this.hidden = false;
26352
26353     Roo.TabPanelItem.superclass.constructor.call(this);
26354 };
26355
26356 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26357     purgeListeners : function(){
26358        Roo.util.Observable.prototype.purgeListeners.call(this);
26359        this.el.removeAllListeners();
26360     },
26361     /**
26362      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26363      */
26364     show : function(){
26365         this.pnode.addClass("on");
26366         this.showAction();
26367         if(Roo.isOpera){
26368             this.tabPanel.stripWrap.repaint();
26369         }
26370         this.fireEvent("activate", this.tabPanel, this);
26371     },
26372
26373     /**
26374      * Returns true if this tab is the active tab.
26375      * @return {Boolean}
26376      */
26377     isActive : function(){
26378         return this.tabPanel.getActiveTab() == this;
26379     },
26380
26381     /**
26382      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
26383      */
26384     hide : function(){
26385         this.pnode.removeClass("on");
26386         this.hideAction();
26387         this.fireEvent("deactivate", this.tabPanel, this);
26388     },
26389
26390     hideAction : function(){
26391         this.bodyEl.hide();
26392         this.bodyEl.setStyle("position", "absolute");
26393         this.bodyEl.setLeft("-20000px");
26394         this.bodyEl.setTop("-20000px");
26395     },
26396
26397     showAction : function(){
26398         this.bodyEl.setStyle("position", "relative");
26399         this.bodyEl.setTop("");
26400         this.bodyEl.setLeft("");
26401         this.bodyEl.show();
26402     },
26403
26404     /**
26405      * Set the tooltip for the tab.
26406      * @param {String} tooltip The tab's tooltip
26407      */
26408     setTooltip : function(text){
26409         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
26410             this.textEl.dom.qtip = text;
26411             this.textEl.dom.removeAttribute('title');
26412         }else{
26413             this.textEl.dom.title = text;
26414         }
26415     },
26416
26417     onTabClick : function(e){
26418         e.preventDefault();
26419         this.tabPanel.activate(this.id);
26420     },
26421
26422     onTabMouseDown : function(e){
26423         e.preventDefault();
26424         this.tabPanel.activate(this.id);
26425     },
26426
26427     getWidth : function(){
26428         return this.inner.getWidth();
26429     },
26430
26431     setWidth : function(width){
26432         var iwidth = width - this.pnode.getPadding("lr");
26433         this.inner.setWidth(iwidth);
26434         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
26435         this.pnode.setWidth(width);
26436     },
26437
26438     /**
26439      * Show or hide the tab
26440      * @param {Boolean} hidden True to hide or false to show.
26441      */
26442     setHidden : function(hidden){
26443         this.hidden = hidden;
26444         this.pnode.setStyle("display", hidden ? "none" : "");
26445     },
26446
26447     /**
26448      * Returns true if this tab is "hidden"
26449      * @return {Boolean}
26450      */
26451     isHidden : function(){
26452         return this.hidden;
26453     },
26454
26455     /**
26456      * Returns the text for this tab
26457      * @return {String}
26458      */
26459     getText : function(){
26460         return this.text;
26461     },
26462
26463     autoSize : function(){
26464         //this.el.beginMeasure();
26465         this.textEl.setWidth(1);
26466         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
26467         //this.el.endMeasure();
26468     },
26469
26470     /**
26471      * Sets the text for the tab (Note: this also sets the tooltip text)
26472      * @param {String} text The tab's text and tooltip
26473      */
26474     setText : function(text){
26475         this.text = text;
26476         this.textEl.update(text);
26477         this.setTooltip(text);
26478         if(!this.tabPanel.resizeTabs){
26479             this.autoSize();
26480         }
26481     },
26482     /**
26483      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
26484      */
26485     activate : function(){
26486         this.tabPanel.activate(this.id);
26487     },
26488
26489     /**
26490      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
26491      */
26492     disable : function(){
26493         if(this.tabPanel.active != this){
26494             this.disabled = true;
26495             this.pnode.addClass("disabled");
26496         }
26497     },
26498
26499     /**
26500      * Enables this TabPanelItem if it was previously disabled.
26501      */
26502     enable : function(){
26503         this.disabled = false;
26504         this.pnode.removeClass("disabled");
26505     },
26506
26507     /**
26508      * Sets the content for this TabPanelItem.
26509      * @param {String} content The content
26510      * @param {Boolean} loadScripts true to look for and load scripts
26511      */
26512     setContent : function(content, loadScripts){
26513         this.bodyEl.update(content, loadScripts);
26514     },
26515
26516     /**
26517      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
26518      * @return {Roo.UpdateManager} The UpdateManager
26519      */
26520     getUpdateManager : function(){
26521         return this.bodyEl.getUpdateManager();
26522     },
26523
26524     /**
26525      * Set a URL to be used to load the content for this TabPanelItem.
26526      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
26527      * @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)
26528      * @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)
26529      * @return {Roo.UpdateManager} The UpdateManager
26530      */
26531     setUrl : function(url, params, loadOnce){
26532         if(this.refreshDelegate){
26533             this.un('activate', this.refreshDelegate);
26534         }
26535         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
26536         this.on("activate", this.refreshDelegate);
26537         return this.bodyEl.getUpdateManager();
26538     },
26539
26540     /** @private */
26541     _handleRefresh : function(url, params, loadOnce){
26542         if(!loadOnce || !this.loaded){
26543             var updater = this.bodyEl.getUpdateManager();
26544             updater.update(url, params, this._setLoaded.createDelegate(this));
26545         }
26546     },
26547
26548     /**
26549      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
26550      *   Will fail silently if the setUrl method has not been called.
26551      *   This does not activate the panel, just updates its content.
26552      */
26553     refresh : function(){
26554         if(this.refreshDelegate){
26555            this.loaded = false;
26556            this.refreshDelegate();
26557         }
26558     },
26559
26560     /** @private */
26561     _setLoaded : function(){
26562         this.loaded = true;
26563     },
26564
26565     /** @private */
26566     closeClick : function(e){
26567         var o = {};
26568         e.stopEvent();
26569         this.fireEvent("beforeclose", this, o);
26570         if(o.cancel !== true){
26571             this.tabPanel.removeTab(this.id);
26572         }
26573     },
26574     /**
26575      * The text displayed in the tooltip for the close icon.
26576      * @type String
26577      */
26578     closeText : "Close this tab"
26579 });
26580
26581 /** @private */
26582 Roo.TabPanel.prototype.createStrip = function(container){
26583     var strip = document.createElement("div");
26584     strip.className = "x-tabs-wrap";
26585     container.appendChild(strip);
26586     return strip;
26587 };
26588 /** @private */
26589 Roo.TabPanel.prototype.createStripList = function(strip){
26590     // div wrapper for retard IE
26591     // returns the "tr" element.
26592     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26593         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26594         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26595     return strip.firstChild.firstChild.firstChild.firstChild;
26596 };
26597 /** @private */
26598 Roo.TabPanel.prototype.createBody = function(container){
26599     var body = document.createElement("div");
26600     Roo.id(body, "tab-body");
26601     Roo.fly(body).addClass("x-tabs-body");
26602     container.appendChild(body);
26603     return body;
26604 };
26605 /** @private */
26606 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26607     var body = Roo.getDom(id);
26608     if(!body){
26609         body = document.createElement("div");
26610         body.id = id;
26611     }
26612     Roo.fly(body).addClass("x-tabs-item-body");
26613     bodyEl.insertBefore(body, bodyEl.firstChild);
26614     return body;
26615 };
26616 /** @private */
26617 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26618     var td = document.createElement("td");
26619     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26620     //stripEl.appendChild(td);
26621     if(closable){
26622         td.className = "x-tabs-closable";
26623         if(!this.closeTpl){
26624             this.closeTpl = new Roo.Template(
26625                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26626                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26627                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26628             );
26629         }
26630         var el = this.closeTpl.overwrite(td, {"text": text});
26631         var close = el.getElementsByTagName("div")[0];
26632         var inner = el.getElementsByTagName("em")[0];
26633         return {"el": el, "close": close, "inner": inner};
26634     } else {
26635         if(!this.tabTpl){
26636             this.tabTpl = new Roo.Template(
26637                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26638                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26639             );
26640         }
26641         var el = this.tabTpl.overwrite(td, {"text": text});
26642         var inner = el.getElementsByTagName("em")[0];
26643         return {"el": el, "inner": inner};
26644     }
26645 };/*
26646  * Based on:
26647  * Ext JS Library 1.1.1
26648  * Copyright(c) 2006-2007, Ext JS, LLC.
26649  *
26650  * Originally Released Under LGPL - original licence link has changed is not relivant.
26651  *
26652  * Fork - LGPL
26653  * <script type="text/javascript">
26654  */
26655
26656 /**
26657  * @class Roo.Button
26658  * @extends Roo.util.Observable
26659  * Simple Button class
26660  * @cfg {String} text The button text
26661  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26662  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26663  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26664  * @cfg {Object} scope The scope of the handler
26665  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26666  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26667  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26668  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26669  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26670  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26671    applies if enableToggle = true)
26672  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26673  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26674   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26675  * @constructor
26676  * Create a new button
26677  * @param {Object} config The config object
26678  */
26679 Roo.Button = function(renderTo, config)
26680 {
26681     if (!config) {
26682         config = renderTo;
26683         renderTo = config.renderTo || false;
26684     }
26685     
26686     Roo.apply(this, config);
26687     this.addEvents({
26688         /**
26689              * @event click
26690              * Fires when this button is clicked
26691              * @param {Button} this
26692              * @param {EventObject} e The click event
26693              */
26694             "click" : true,
26695         /**
26696              * @event toggle
26697              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26698              * @param {Button} this
26699              * @param {Boolean} pressed
26700              */
26701             "toggle" : true,
26702         /**
26703              * @event mouseover
26704              * Fires when the mouse hovers over the button
26705              * @param {Button} this
26706              * @param {Event} e The event object
26707              */
26708         'mouseover' : true,
26709         /**
26710              * @event mouseout
26711              * Fires when the mouse exits the button
26712              * @param {Button} this
26713              * @param {Event} e The event object
26714              */
26715         'mouseout': true,
26716          /**
26717              * @event render
26718              * Fires when the button is rendered
26719              * @param {Button} this
26720              */
26721         'render': true
26722     });
26723     if(this.menu){
26724         this.menu = Roo.menu.MenuMgr.get(this.menu);
26725     }
26726     // register listeners first!!  - so render can be captured..
26727     Roo.util.Observable.call(this);
26728     if(renderTo){
26729         this.render(renderTo);
26730     }
26731     
26732   
26733 };
26734
26735 Roo.extend(Roo.Button, Roo.util.Observable, {
26736     /**
26737      * 
26738      */
26739     
26740     /**
26741      * Read-only. True if this button is hidden
26742      * @type Boolean
26743      */
26744     hidden : false,
26745     /**
26746      * Read-only. True if this button is disabled
26747      * @type Boolean
26748      */
26749     disabled : false,
26750     /**
26751      * Read-only. True if this button is pressed (only if enableToggle = true)
26752      * @type Boolean
26753      */
26754     pressed : false,
26755
26756     /**
26757      * @cfg {Number} tabIndex 
26758      * The DOM tabIndex for this button (defaults to undefined)
26759      */
26760     tabIndex : undefined,
26761
26762     /**
26763      * @cfg {Boolean} enableToggle
26764      * True to enable pressed/not pressed toggling (defaults to false)
26765      */
26766     enableToggle: false,
26767     /**
26768      * @cfg {Mixed} menu
26769      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26770      */
26771     menu : undefined,
26772     /**
26773      * @cfg {String} menuAlign
26774      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26775      */
26776     menuAlign : "tl-bl?",
26777
26778     /**
26779      * @cfg {String} iconCls
26780      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26781      */
26782     iconCls : undefined,
26783     /**
26784      * @cfg {String} type
26785      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26786      */
26787     type : 'button',
26788
26789     // private
26790     menuClassTarget: 'tr',
26791
26792     /**
26793      * @cfg {String} clickEvent
26794      * The type of event to map to the button's event handler (defaults to 'click')
26795      */
26796     clickEvent : 'click',
26797
26798     /**
26799      * @cfg {Boolean} handleMouseEvents
26800      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26801      */
26802     handleMouseEvents : true,
26803
26804     /**
26805      * @cfg {String} tooltipType
26806      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26807      */
26808     tooltipType : 'qtip',
26809
26810     /**
26811      * @cfg {String} cls
26812      * A CSS class to apply to the button's main element.
26813      */
26814     
26815     /**
26816      * @cfg {Roo.Template} template (Optional)
26817      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26818      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26819      * require code modifications if required elements (e.g. a button) aren't present.
26820      */
26821
26822     // private
26823     render : function(renderTo){
26824         var btn;
26825         if(this.hideParent){
26826             this.parentEl = Roo.get(renderTo);
26827         }
26828         if(!this.dhconfig){
26829             if(!this.template){
26830                 if(!Roo.Button.buttonTemplate){
26831                     // hideous table template
26832                     Roo.Button.buttonTemplate = new Roo.Template(
26833                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26834                         '<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>',
26835                         "</tr></tbody></table>");
26836                 }
26837                 this.template = Roo.Button.buttonTemplate;
26838             }
26839             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26840             var btnEl = btn.child("button:first");
26841             btnEl.on('focus', this.onFocus, this);
26842             btnEl.on('blur', this.onBlur, this);
26843             if(this.cls){
26844                 btn.addClass(this.cls);
26845             }
26846             if(this.icon){
26847                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26848             }
26849             if(this.iconCls){
26850                 btnEl.addClass(this.iconCls);
26851                 if(!this.cls){
26852                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26853                 }
26854             }
26855             if(this.tabIndex !== undefined){
26856                 btnEl.dom.tabIndex = this.tabIndex;
26857             }
26858             if(this.tooltip){
26859                 if(typeof this.tooltip == 'object'){
26860                     Roo.QuickTips.tips(Roo.apply({
26861                           target: btnEl.id
26862                     }, this.tooltip));
26863                 } else {
26864                     btnEl.dom[this.tooltipType] = this.tooltip;
26865                 }
26866             }
26867         }else{
26868             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26869         }
26870         this.el = btn;
26871         if(this.id){
26872             this.el.dom.id = this.el.id = this.id;
26873         }
26874         if(this.menu){
26875             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26876             this.menu.on("show", this.onMenuShow, this);
26877             this.menu.on("hide", this.onMenuHide, this);
26878         }
26879         btn.addClass("x-btn");
26880         if(Roo.isIE && !Roo.isIE7){
26881             this.autoWidth.defer(1, this);
26882         }else{
26883             this.autoWidth();
26884         }
26885         if(this.handleMouseEvents){
26886             btn.on("mouseover", this.onMouseOver, this);
26887             btn.on("mouseout", this.onMouseOut, this);
26888             btn.on("mousedown", this.onMouseDown, this);
26889         }
26890         btn.on(this.clickEvent, this.onClick, this);
26891         //btn.on("mouseup", this.onMouseUp, this);
26892         if(this.hidden){
26893             this.hide();
26894         }
26895         if(this.disabled){
26896             this.disable();
26897         }
26898         Roo.ButtonToggleMgr.register(this);
26899         if(this.pressed){
26900             this.el.addClass("x-btn-pressed");
26901         }
26902         if(this.repeat){
26903             var repeater = new Roo.util.ClickRepeater(btn,
26904                 typeof this.repeat == "object" ? this.repeat : {}
26905             );
26906             repeater.on("click", this.onClick,  this);
26907         }
26908         
26909         this.fireEvent('render', this);
26910         
26911     },
26912     /**
26913      * Returns the button's underlying element
26914      * @return {Roo.Element} The element
26915      */
26916     getEl : function(){
26917         return this.el;  
26918     },
26919     
26920     /**
26921      * Destroys this Button and removes any listeners.
26922      */
26923     destroy : function(){
26924         Roo.ButtonToggleMgr.unregister(this);
26925         this.el.removeAllListeners();
26926         this.purgeListeners();
26927         this.el.remove();
26928     },
26929
26930     // private
26931     autoWidth : function(){
26932         if(this.el){
26933             this.el.setWidth("auto");
26934             if(Roo.isIE7 && Roo.isStrict){
26935                 var ib = this.el.child('button');
26936                 if(ib && ib.getWidth() > 20){
26937                     ib.clip();
26938                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26939                 }
26940             }
26941             if(this.minWidth){
26942                 if(this.hidden){
26943                     this.el.beginMeasure();
26944                 }
26945                 if(this.el.getWidth() < this.minWidth){
26946                     this.el.setWidth(this.minWidth);
26947                 }
26948                 if(this.hidden){
26949                     this.el.endMeasure();
26950                 }
26951             }
26952         }
26953     },
26954
26955     /**
26956      * Assigns this button's click handler
26957      * @param {Function} handler The function to call when the button is clicked
26958      * @param {Object} scope (optional) Scope for the function passed in
26959      */
26960     setHandler : function(handler, scope){
26961         this.handler = handler;
26962         this.scope = scope;  
26963     },
26964     
26965     /**
26966      * Sets this button's text
26967      * @param {String} text The button text
26968      */
26969     setText : function(text){
26970         this.text = text;
26971         if(this.el){
26972             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26973         }
26974         this.autoWidth();
26975     },
26976     
26977     /**
26978      * Gets the text for this button
26979      * @return {String} The button text
26980      */
26981     getText : function(){
26982         return this.text;  
26983     },
26984     
26985     /**
26986      * Show this button
26987      */
26988     show: function(){
26989         this.hidden = false;
26990         if(this.el){
26991             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26992         }
26993     },
26994     
26995     /**
26996      * Hide this button
26997      */
26998     hide: function(){
26999         this.hidden = true;
27000         if(this.el){
27001             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27002         }
27003     },
27004     
27005     /**
27006      * Convenience function for boolean show/hide
27007      * @param {Boolean} visible True to show, false to hide
27008      */
27009     setVisible: function(visible){
27010         if(visible) {
27011             this.show();
27012         }else{
27013             this.hide();
27014         }
27015     },
27016     
27017     /**
27018      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27019      * @param {Boolean} state (optional) Force a particular state
27020      */
27021     toggle : function(state){
27022         state = state === undefined ? !this.pressed : state;
27023         if(state != this.pressed){
27024             if(state){
27025                 this.el.addClass("x-btn-pressed");
27026                 this.pressed = true;
27027                 this.fireEvent("toggle", this, true);
27028             }else{
27029                 this.el.removeClass("x-btn-pressed");
27030                 this.pressed = false;
27031                 this.fireEvent("toggle", this, false);
27032             }
27033             if(this.toggleHandler){
27034                 this.toggleHandler.call(this.scope || this, this, state);
27035             }
27036         }
27037     },
27038     
27039     /**
27040      * Focus the button
27041      */
27042     focus : function(){
27043         this.el.child('button:first').focus();
27044     },
27045     
27046     /**
27047      * Disable this button
27048      */
27049     disable : function(){
27050         if(this.el){
27051             this.el.addClass("x-btn-disabled");
27052         }
27053         this.disabled = true;
27054     },
27055     
27056     /**
27057      * Enable this button
27058      */
27059     enable : function(){
27060         if(this.el){
27061             this.el.removeClass("x-btn-disabled");
27062         }
27063         this.disabled = false;
27064     },
27065
27066     /**
27067      * Convenience function for boolean enable/disable
27068      * @param {Boolean} enabled True to enable, false to disable
27069      */
27070     setDisabled : function(v){
27071         this[v !== true ? "enable" : "disable"]();
27072     },
27073
27074     // private
27075     onClick : function(e){
27076         if(e){
27077             e.preventDefault();
27078         }
27079         if(e.button != 0){
27080             return;
27081         }
27082         if(!this.disabled){
27083             if(this.enableToggle){
27084                 this.toggle();
27085             }
27086             if(this.menu && !this.menu.isVisible()){
27087                 this.menu.show(this.el, this.menuAlign);
27088             }
27089             this.fireEvent("click", this, e);
27090             if(this.handler){
27091                 this.el.removeClass("x-btn-over");
27092                 this.handler.call(this.scope || this, this, e);
27093             }
27094         }
27095     },
27096     // private
27097     onMouseOver : function(e){
27098         if(!this.disabled){
27099             this.el.addClass("x-btn-over");
27100             this.fireEvent('mouseover', this, e);
27101         }
27102     },
27103     // private
27104     onMouseOut : function(e){
27105         if(!e.within(this.el,  true)){
27106             this.el.removeClass("x-btn-over");
27107             this.fireEvent('mouseout', this, e);
27108         }
27109     },
27110     // private
27111     onFocus : function(e){
27112         if(!this.disabled){
27113             this.el.addClass("x-btn-focus");
27114         }
27115     },
27116     // private
27117     onBlur : function(e){
27118         this.el.removeClass("x-btn-focus");
27119     },
27120     // private
27121     onMouseDown : function(e){
27122         if(!this.disabled && e.button == 0){
27123             this.el.addClass("x-btn-click");
27124             Roo.get(document).on('mouseup', this.onMouseUp, this);
27125         }
27126     },
27127     // private
27128     onMouseUp : function(e){
27129         if(e.button == 0){
27130             this.el.removeClass("x-btn-click");
27131             Roo.get(document).un('mouseup', this.onMouseUp, this);
27132         }
27133     },
27134     // private
27135     onMenuShow : function(e){
27136         this.el.addClass("x-btn-menu-active");
27137     },
27138     // private
27139     onMenuHide : function(e){
27140         this.el.removeClass("x-btn-menu-active");
27141     }   
27142 });
27143
27144 // Private utility class used by Button
27145 Roo.ButtonToggleMgr = function(){
27146    var groups = {};
27147    
27148    function toggleGroup(btn, state){
27149        if(state){
27150            var g = groups[btn.toggleGroup];
27151            for(var i = 0, l = g.length; i < l; i++){
27152                if(g[i] != btn){
27153                    g[i].toggle(false);
27154                }
27155            }
27156        }
27157    }
27158    
27159    return {
27160        register : function(btn){
27161            if(!btn.toggleGroup){
27162                return;
27163            }
27164            var g = groups[btn.toggleGroup];
27165            if(!g){
27166                g = groups[btn.toggleGroup] = [];
27167            }
27168            g.push(btn);
27169            btn.on("toggle", toggleGroup);
27170        },
27171        
27172        unregister : function(btn){
27173            if(!btn.toggleGroup){
27174                return;
27175            }
27176            var g = groups[btn.toggleGroup];
27177            if(g){
27178                g.remove(btn);
27179                btn.un("toggle", toggleGroup);
27180            }
27181        }
27182    };
27183 }();/*
27184  * Based on:
27185  * Ext JS Library 1.1.1
27186  * Copyright(c) 2006-2007, Ext JS, LLC.
27187  *
27188  * Originally Released Under LGPL - original licence link has changed is not relivant.
27189  *
27190  * Fork - LGPL
27191  * <script type="text/javascript">
27192  */
27193  
27194 /**
27195  * @class Roo.SplitButton
27196  * @extends Roo.Button
27197  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27198  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27199  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27200  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27201  * @cfg {String} arrowTooltip The title attribute of the arrow
27202  * @constructor
27203  * Create a new menu button
27204  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27205  * @param {Object} config The config object
27206  */
27207 Roo.SplitButton = function(renderTo, config){
27208     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27209     /**
27210      * @event arrowclick
27211      * Fires when this button's arrow is clicked
27212      * @param {SplitButton} this
27213      * @param {EventObject} e The click event
27214      */
27215     this.addEvents({"arrowclick":true});
27216 };
27217
27218 Roo.extend(Roo.SplitButton, Roo.Button, {
27219     render : function(renderTo){
27220         // this is one sweet looking template!
27221         var tpl = new Roo.Template(
27222             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27223             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27224             '<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>',
27225             "</tbody></table></td><td>",
27226             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27227             '<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>',
27228             "</tbody></table></td></tr></table>"
27229         );
27230         var btn = tpl.append(renderTo, [this.text, this.type], true);
27231         var btnEl = btn.child("button");
27232         if(this.cls){
27233             btn.addClass(this.cls);
27234         }
27235         if(this.icon){
27236             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27237         }
27238         if(this.iconCls){
27239             btnEl.addClass(this.iconCls);
27240             if(!this.cls){
27241                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27242             }
27243         }
27244         this.el = btn;
27245         if(this.handleMouseEvents){
27246             btn.on("mouseover", this.onMouseOver, this);
27247             btn.on("mouseout", this.onMouseOut, this);
27248             btn.on("mousedown", this.onMouseDown, this);
27249             btn.on("mouseup", this.onMouseUp, this);
27250         }
27251         btn.on(this.clickEvent, this.onClick, this);
27252         if(this.tooltip){
27253             if(typeof this.tooltip == 'object'){
27254                 Roo.QuickTips.tips(Roo.apply({
27255                       target: btnEl.id
27256                 }, this.tooltip));
27257             } else {
27258                 btnEl.dom[this.tooltipType] = this.tooltip;
27259             }
27260         }
27261         if(this.arrowTooltip){
27262             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27263         }
27264         if(this.hidden){
27265             this.hide();
27266         }
27267         if(this.disabled){
27268             this.disable();
27269         }
27270         if(this.pressed){
27271             this.el.addClass("x-btn-pressed");
27272         }
27273         if(Roo.isIE && !Roo.isIE7){
27274             this.autoWidth.defer(1, this);
27275         }else{
27276             this.autoWidth();
27277         }
27278         if(this.menu){
27279             this.menu.on("show", this.onMenuShow, this);
27280             this.menu.on("hide", this.onMenuHide, this);
27281         }
27282         this.fireEvent('render', this);
27283     },
27284
27285     // private
27286     autoWidth : function(){
27287         if(this.el){
27288             var tbl = this.el.child("table:first");
27289             var tbl2 = this.el.child("table:last");
27290             this.el.setWidth("auto");
27291             tbl.setWidth("auto");
27292             if(Roo.isIE7 && Roo.isStrict){
27293                 var ib = this.el.child('button:first');
27294                 if(ib && ib.getWidth() > 20){
27295                     ib.clip();
27296                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27297                 }
27298             }
27299             if(this.minWidth){
27300                 if(this.hidden){
27301                     this.el.beginMeasure();
27302                 }
27303                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27304                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27305                 }
27306                 if(this.hidden){
27307                     this.el.endMeasure();
27308                 }
27309             }
27310             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27311         } 
27312     },
27313     /**
27314      * Sets this button's click handler
27315      * @param {Function} handler The function to call when the button is clicked
27316      * @param {Object} scope (optional) Scope for the function passed above
27317      */
27318     setHandler : function(handler, scope){
27319         this.handler = handler;
27320         this.scope = scope;  
27321     },
27322     
27323     /**
27324      * Sets this button's arrow click handler
27325      * @param {Function} handler The function to call when the arrow is clicked
27326      * @param {Object} scope (optional) Scope for the function passed above
27327      */
27328     setArrowHandler : function(handler, scope){
27329         this.arrowHandler = handler;
27330         this.scope = scope;  
27331     },
27332     
27333     /**
27334      * Focus the button
27335      */
27336     focus : function(){
27337         if(this.el){
27338             this.el.child("button:first").focus();
27339         }
27340     },
27341
27342     // private
27343     onClick : function(e){
27344         e.preventDefault();
27345         if(!this.disabled){
27346             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27347                 if(this.menu && !this.menu.isVisible()){
27348                     this.menu.show(this.el, this.menuAlign);
27349                 }
27350                 this.fireEvent("arrowclick", this, e);
27351                 if(this.arrowHandler){
27352                     this.arrowHandler.call(this.scope || this, this, e);
27353                 }
27354             }else{
27355                 this.fireEvent("click", this, e);
27356                 if(this.handler){
27357                     this.handler.call(this.scope || this, this, e);
27358                 }
27359             }
27360         }
27361     },
27362     // private
27363     onMouseDown : function(e){
27364         if(!this.disabled){
27365             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27366         }
27367     },
27368     // private
27369     onMouseUp : function(e){
27370         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27371     }   
27372 });
27373
27374
27375 // backwards compat
27376 Roo.MenuButton = Roo.SplitButton;/*
27377  * Based on:
27378  * Ext JS Library 1.1.1
27379  * Copyright(c) 2006-2007, Ext JS, LLC.
27380  *
27381  * Originally Released Under LGPL - original licence link has changed is not relivant.
27382  *
27383  * Fork - LGPL
27384  * <script type="text/javascript">
27385  */
27386
27387 /**
27388  * @class Roo.Toolbar
27389  * Basic Toolbar class.
27390  * @constructor
27391  * Creates a new Toolbar
27392  * @param {Object} container The config object
27393  */ 
27394 Roo.Toolbar = function(container, buttons, config)
27395 {
27396     /// old consturctor format still supported..
27397     if(container instanceof Array){ // omit the container for later rendering
27398         buttons = container;
27399         config = buttons;
27400         container = null;
27401     }
27402     if (typeof(container) == 'object' && container.xtype) {
27403         config = container;
27404         container = config.container;
27405         buttons = config.buttons || []; // not really - use items!!
27406     }
27407     var xitems = [];
27408     if (config && config.items) {
27409         xitems = config.items;
27410         delete config.items;
27411     }
27412     Roo.apply(this, config);
27413     this.buttons = buttons;
27414     
27415     if(container){
27416         this.render(container);
27417     }
27418     this.xitems = xitems;
27419     Roo.each(xitems, function(b) {
27420         this.add(b);
27421     }, this);
27422     
27423 };
27424
27425 Roo.Toolbar.prototype = {
27426     /**
27427      * @cfg {Array} items
27428      * array of button configs or elements to add (will be converted to a MixedCollection)
27429      */
27430     
27431     /**
27432      * @cfg {String/HTMLElement/Element} container
27433      * The id or element that will contain the toolbar
27434      */
27435     // private
27436     render : function(ct){
27437         this.el = Roo.get(ct);
27438         if(this.cls){
27439             this.el.addClass(this.cls);
27440         }
27441         // using a table allows for vertical alignment
27442         // 100% width is needed by Safari...
27443         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
27444         this.tr = this.el.child("tr", true);
27445         var autoId = 0;
27446         this.items = new Roo.util.MixedCollection(false, function(o){
27447             return o.id || ("item" + (++autoId));
27448         });
27449         if(this.buttons){
27450             this.add.apply(this, this.buttons);
27451             delete this.buttons;
27452         }
27453     },
27454
27455     /**
27456      * Adds element(s) to the toolbar -- this function takes a variable number of 
27457      * arguments of mixed type and adds them to the toolbar.
27458      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
27459      * <ul>
27460      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
27461      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
27462      * <li>Field: Any form field (equivalent to {@link #addField})</li>
27463      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
27464      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
27465      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
27466      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
27467      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
27468      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
27469      * </ul>
27470      * @param {Mixed} arg2
27471      * @param {Mixed} etc.
27472      */
27473     add : function(){
27474         var a = arguments, l = a.length;
27475         for(var i = 0; i < l; i++){
27476             this._add(a[i]);
27477         }
27478     },
27479     // private..
27480     _add : function(el) {
27481         
27482         if (el.xtype) {
27483             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
27484         }
27485         
27486         if (el.applyTo){ // some kind of form field
27487             return this.addField(el);
27488         } 
27489         if (el.render){ // some kind of Toolbar.Item
27490             return this.addItem(el);
27491         }
27492         if (typeof el == "string"){ // string
27493             if(el == "separator" || el == "-"){
27494                 return this.addSeparator();
27495             }
27496             if (el == " "){
27497                 return this.addSpacer();
27498             }
27499             if(el == "->"){
27500                 return this.addFill();
27501             }
27502             return this.addText(el);
27503             
27504         }
27505         if(el.tagName){ // element
27506             return this.addElement(el);
27507         }
27508         if(typeof el == "object"){ // must be button config?
27509             return this.addButton(el);
27510         }
27511         // and now what?!?!
27512         return false;
27513         
27514     },
27515     
27516     /**
27517      * Add an Xtype element
27518      * @param {Object} xtype Xtype Object
27519      * @return {Object} created Object
27520      */
27521     addxtype : function(e){
27522         return this.add(e);  
27523     },
27524     
27525     /**
27526      * Returns the Element for this toolbar.
27527      * @return {Roo.Element}
27528      */
27529     getEl : function(){
27530         return this.el;  
27531     },
27532     
27533     /**
27534      * Adds a separator
27535      * @return {Roo.Toolbar.Item} The separator item
27536      */
27537     addSeparator : function(){
27538         return this.addItem(new Roo.Toolbar.Separator());
27539     },
27540
27541     /**
27542      * Adds a spacer element
27543      * @return {Roo.Toolbar.Spacer} The spacer item
27544      */
27545     addSpacer : function(){
27546         return this.addItem(new Roo.Toolbar.Spacer());
27547     },
27548
27549     /**
27550      * Adds a fill element that forces subsequent additions to the right side of the toolbar
27551      * @return {Roo.Toolbar.Fill} The fill item
27552      */
27553     addFill : function(){
27554         return this.addItem(new Roo.Toolbar.Fill());
27555     },
27556
27557     /**
27558      * Adds any standard HTML element to the toolbar
27559      * @param {String/HTMLElement/Element} el The element or id of the element to add
27560      * @return {Roo.Toolbar.Item} The element's item
27561      */
27562     addElement : function(el){
27563         return this.addItem(new Roo.Toolbar.Item(el));
27564     },
27565     /**
27566      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
27567      * @type Roo.util.MixedCollection  
27568      */
27569     items : false,
27570      
27571     /**
27572      * Adds any Toolbar.Item or subclass
27573      * @param {Roo.Toolbar.Item} item
27574      * @return {Roo.Toolbar.Item} The item
27575      */
27576     addItem : function(item){
27577         var td = this.nextBlock();
27578         item.render(td);
27579         this.items.add(item);
27580         return item;
27581     },
27582     
27583     /**
27584      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
27585      * @param {Object/Array} config A button config or array of configs
27586      * @return {Roo.Toolbar.Button/Array}
27587      */
27588     addButton : function(config){
27589         if(config instanceof Array){
27590             var buttons = [];
27591             for(var i = 0, len = config.length; i < len; i++) {
27592                 buttons.push(this.addButton(config[i]));
27593             }
27594             return buttons;
27595         }
27596         var b = config;
27597         if(!(config instanceof Roo.Toolbar.Button)){
27598             b = config.split ?
27599                 new Roo.Toolbar.SplitButton(config) :
27600                 new Roo.Toolbar.Button(config);
27601         }
27602         var td = this.nextBlock();
27603         b.render(td);
27604         this.items.add(b);
27605         return b;
27606     },
27607     
27608     /**
27609      * Adds text to the toolbar
27610      * @param {String} text The text to add
27611      * @return {Roo.Toolbar.Item} The element's item
27612      */
27613     addText : function(text){
27614         return this.addItem(new Roo.Toolbar.TextItem(text));
27615     },
27616     
27617     /**
27618      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27619      * @param {Number} index The index where the item is to be inserted
27620      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27621      * @return {Roo.Toolbar.Button/Item}
27622      */
27623     insertButton : function(index, item){
27624         if(item instanceof Array){
27625             var buttons = [];
27626             for(var i = 0, len = item.length; i < len; i++) {
27627                buttons.push(this.insertButton(index + i, item[i]));
27628             }
27629             return buttons;
27630         }
27631         if (!(item instanceof Roo.Toolbar.Button)){
27632            item = new Roo.Toolbar.Button(item);
27633         }
27634         var td = document.createElement("td");
27635         this.tr.insertBefore(td, this.tr.childNodes[index]);
27636         item.render(td);
27637         this.items.insert(index, item);
27638         return item;
27639     },
27640     
27641     /**
27642      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27643      * @param {Object} config
27644      * @return {Roo.Toolbar.Item} The element's item
27645      */
27646     addDom : function(config, returnEl){
27647         var td = this.nextBlock();
27648         Roo.DomHelper.overwrite(td, config);
27649         var ti = new Roo.Toolbar.Item(td.firstChild);
27650         ti.render(td);
27651         this.items.add(ti);
27652         return ti;
27653     },
27654
27655     /**
27656      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27657      * @type Roo.util.MixedCollection  
27658      */
27659     fields : false,
27660     
27661     /**
27662      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27663      * Note: the field should not have been rendered yet. For a field that has already been
27664      * rendered, use {@link #addElement}.
27665      * @param {Roo.form.Field} field
27666      * @return {Roo.ToolbarItem}
27667      */
27668      
27669       
27670     addField : function(field) {
27671         if (!this.fields) {
27672             var autoId = 0;
27673             this.fields = new Roo.util.MixedCollection(false, function(o){
27674                 return o.id || ("item" + (++autoId));
27675             });
27676
27677         }
27678         
27679         var td = this.nextBlock();
27680         field.render(td);
27681         var ti = new Roo.Toolbar.Item(td.firstChild);
27682         ti.render(td);
27683         this.items.add(ti);
27684         this.fields.add(field);
27685         return ti;
27686     },
27687     /**
27688      * Hide the toolbar
27689      * @method hide
27690      */
27691      
27692       
27693     hide : function()
27694     {
27695         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27696         this.el.child('div').hide();
27697     },
27698     /**
27699      * Show the toolbar
27700      * @method show
27701      */
27702     show : function()
27703     {
27704         this.el.child('div').show();
27705     },
27706       
27707     // private
27708     nextBlock : function(){
27709         var td = document.createElement("td");
27710         this.tr.appendChild(td);
27711         return td;
27712     },
27713
27714     // private
27715     destroy : function(){
27716         if(this.items){ // rendered?
27717             Roo.destroy.apply(Roo, this.items.items);
27718         }
27719         if(this.fields){ // rendered?
27720             Roo.destroy.apply(Roo, this.fields.items);
27721         }
27722         Roo.Element.uncache(this.el, this.tr);
27723     }
27724 };
27725
27726 /**
27727  * @class Roo.Toolbar.Item
27728  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27729  * @constructor
27730  * Creates a new Item
27731  * @param {HTMLElement} el 
27732  */
27733 Roo.Toolbar.Item = function(el){
27734     this.el = Roo.getDom(el);
27735     this.id = Roo.id(this.el);
27736     this.hidden = false;
27737 };
27738
27739 Roo.Toolbar.Item.prototype = {
27740     
27741     /**
27742      * Get this item's HTML Element
27743      * @return {HTMLElement}
27744      */
27745     getEl : function(){
27746        return this.el;  
27747     },
27748
27749     // private
27750     render : function(td){
27751         this.td = td;
27752         td.appendChild(this.el);
27753     },
27754     
27755     /**
27756      * Removes and destroys this item.
27757      */
27758     destroy : function(){
27759         this.td.parentNode.removeChild(this.td);
27760     },
27761     
27762     /**
27763      * Shows this item.
27764      */
27765     show: function(){
27766         this.hidden = false;
27767         this.td.style.display = "";
27768     },
27769     
27770     /**
27771      * Hides this item.
27772      */
27773     hide: function(){
27774         this.hidden = true;
27775         this.td.style.display = "none";
27776     },
27777     
27778     /**
27779      * Convenience function for boolean show/hide.
27780      * @param {Boolean} visible true to show/false to hide
27781      */
27782     setVisible: function(visible){
27783         if(visible) {
27784             this.show();
27785         }else{
27786             this.hide();
27787         }
27788     },
27789     
27790     /**
27791      * Try to focus this item.
27792      */
27793     focus : function(){
27794         Roo.fly(this.el).focus();
27795     },
27796     
27797     /**
27798      * Disables this item.
27799      */
27800     disable : function(){
27801         Roo.fly(this.td).addClass("x-item-disabled");
27802         this.disabled = true;
27803         this.el.disabled = true;
27804     },
27805     
27806     /**
27807      * Enables this item.
27808      */
27809     enable : function(){
27810         Roo.fly(this.td).removeClass("x-item-disabled");
27811         this.disabled = false;
27812         this.el.disabled = false;
27813     }
27814 };
27815
27816
27817 /**
27818  * @class Roo.Toolbar.Separator
27819  * @extends Roo.Toolbar.Item
27820  * A simple toolbar separator class
27821  * @constructor
27822  * Creates a new Separator
27823  */
27824 Roo.Toolbar.Separator = function(){
27825     var s = document.createElement("span");
27826     s.className = "ytb-sep";
27827     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27828 };
27829 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27830     enable:Roo.emptyFn,
27831     disable:Roo.emptyFn,
27832     focus:Roo.emptyFn
27833 });
27834
27835 /**
27836  * @class Roo.Toolbar.Spacer
27837  * @extends Roo.Toolbar.Item
27838  * A simple element that adds extra horizontal space to a toolbar.
27839  * @constructor
27840  * Creates a new Spacer
27841  */
27842 Roo.Toolbar.Spacer = function(){
27843     var s = document.createElement("div");
27844     s.className = "ytb-spacer";
27845     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27846 };
27847 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27848     enable:Roo.emptyFn,
27849     disable:Roo.emptyFn,
27850     focus:Roo.emptyFn
27851 });
27852
27853 /**
27854  * @class Roo.Toolbar.Fill
27855  * @extends Roo.Toolbar.Spacer
27856  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27857  * @constructor
27858  * Creates a new Spacer
27859  */
27860 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27861     // private
27862     render : function(td){
27863         td.style.width = '100%';
27864         Roo.Toolbar.Fill.superclass.render.call(this, td);
27865     }
27866 });
27867
27868 /**
27869  * @class Roo.Toolbar.TextItem
27870  * @extends Roo.Toolbar.Item
27871  * A simple class that renders text directly into a toolbar.
27872  * @constructor
27873  * Creates a new TextItem
27874  * @param {String} text
27875  */
27876 Roo.Toolbar.TextItem = function(text){
27877     if (typeof(text) == 'object') {
27878         text = text.text;
27879     }
27880     var s = document.createElement("span");
27881     s.className = "ytb-text";
27882     s.innerHTML = text;
27883     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27884 };
27885 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27886     enable:Roo.emptyFn,
27887     disable:Roo.emptyFn,
27888     focus:Roo.emptyFn
27889 });
27890
27891 /**
27892  * @class Roo.Toolbar.Button
27893  * @extends Roo.Button
27894  * A button that renders into a toolbar.
27895  * @constructor
27896  * Creates a new Button
27897  * @param {Object} config A standard {@link Roo.Button} config object
27898  */
27899 Roo.Toolbar.Button = function(config){
27900     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27901 };
27902 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27903     render : function(td){
27904         this.td = td;
27905         Roo.Toolbar.Button.superclass.render.call(this, td);
27906     },
27907     
27908     /**
27909      * Removes and destroys this button
27910      */
27911     destroy : function(){
27912         Roo.Toolbar.Button.superclass.destroy.call(this);
27913         this.td.parentNode.removeChild(this.td);
27914     },
27915     
27916     /**
27917      * Shows this button
27918      */
27919     show: function(){
27920         this.hidden = false;
27921         this.td.style.display = "";
27922     },
27923     
27924     /**
27925      * Hides this button
27926      */
27927     hide: function(){
27928         this.hidden = true;
27929         this.td.style.display = "none";
27930     },
27931
27932     /**
27933      * Disables this item
27934      */
27935     disable : function(){
27936         Roo.fly(this.td).addClass("x-item-disabled");
27937         this.disabled = true;
27938     },
27939
27940     /**
27941      * Enables this item
27942      */
27943     enable : function(){
27944         Roo.fly(this.td).removeClass("x-item-disabled");
27945         this.disabled = false;
27946     }
27947 });
27948 // backwards compat
27949 Roo.ToolbarButton = Roo.Toolbar.Button;
27950
27951 /**
27952  * @class Roo.Toolbar.SplitButton
27953  * @extends Roo.SplitButton
27954  * A menu button that renders into a toolbar.
27955  * @constructor
27956  * Creates a new SplitButton
27957  * @param {Object} config A standard {@link Roo.SplitButton} config object
27958  */
27959 Roo.Toolbar.SplitButton = function(config){
27960     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27961 };
27962 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27963     render : function(td){
27964         this.td = td;
27965         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27966     },
27967     
27968     /**
27969      * Removes and destroys this button
27970      */
27971     destroy : function(){
27972         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27973         this.td.parentNode.removeChild(this.td);
27974     },
27975     
27976     /**
27977      * Shows this button
27978      */
27979     show: function(){
27980         this.hidden = false;
27981         this.td.style.display = "";
27982     },
27983     
27984     /**
27985      * Hides this button
27986      */
27987     hide: function(){
27988         this.hidden = true;
27989         this.td.style.display = "none";
27990     }
27991 });
27992
27993 // backwards compat
27994 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27995  * Based on:
27996  * Ext JS Library 1.1.1
27997  * Copyright(c) 2006-2007, Ext JS, LLC.
27998  *
27999  * Originally Released Under LGPL - original licence link has changed is not relivant.
28000  *
28001  * Fork - LGPL
28002  * <script type="text/javascript">
28003  */
28004  
28005 /**
28006  * @class Roo.PagingToolbar
28007  * @extends Roo.Toolbar
28008  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28009  * @constructor
28010  * Create a new PagingToolbar
28011  * @param {Object} config The config object
28012  */
28013 Roo.PagingToolbar = function(el, ds, config)
28014 {
28015     // old args format still supported... - xtype is prefered..
28016     if (typeof(el) == 'object' && el.xtype) {
28017         // created from xtype...
28018         config = el;
28019         ds = el.dataSource;
28020         el = config.container;
28021     }
28022     var items = [];
28023     if (config.items) {
28024         items = config.items;
28025         config.items = [];
28026     }
28027     
28028     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28029     this.ds = ds;
28030     this.cursor = 0;
28031     this.renderButtons(this.el);
28032     this.bind(ds);
28033     
28034     // supprot items array.
28035    
28036     Roo.each(items, function(e) {
28037         this.add(Roo.factory(e));
28038     },this);
28039     
28040 };
28041
28042 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28043     /**
28044      * @cfg {Roo.data.Store} dataSource
28045      * The underlying data store providing the paged data
28046      */
28047     /**
28048      * @cfg {String/HTMLElement/Element} container
28049      * container The id or element that will contain the toolbar
28050      */
28051     /**
28052      * @cfg {Boolean} displayInfo
28053      * True to display the displayMsg (defaults to false)
28054      */
28055     /**
28056      * @cfg {Number} pageSize
28057      * The number of records to display per page (defaults to 20)
28058      */
28059     pageSize: 20,
28060     /**
28061      * @cfg {String} displayMsg
28062      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28063      */
28064     displayMsg : 'Displaying {0} - {1} of {2}',
28065     /**
28066      * @cfg {String} emptyMsg
28067      * The message to display when no records are found (defaults to "No data to display")
28068      */
28069     emptyMsg : 'No data to display',
28070     /**
28071      * Customizable piece of the default paging text (defaults to "Page")
28072      * @type String
28073      */
28074     beforePageText : "Page",
28075     /**
28076      * Customizable piece of the default paging text (defaults to "of %0")
28077      * @type String
28078      */
28079     afterPageText : "of {0}",
28080     /**
28081      * Customizable piece of the default paging text (defaults to "First Page")
28082      * @type String
28083      */
28084     firstText : "First Page",
28085     /**
28086      * Customizable piece of the default paging text (defaults to "Previous Page")
28087      * @type String
28088      */
28089     prevText : "Previous Page",
28090     /**
28091      * Customizable piece of the default paging text (defaults to "Next Page")
28092      * @type String
28093      */
28094     nextText : "Next Page",
28095     /**
28096      * Customizable piece of the default paging text (defaults to "Last Page")
28097      * @type String
28098      */
28099     lastText : "Last Page",
28100     /**
28101      * Customizable piece of the default paging text (defaults to "Refresh")
28102      * @type String
28103      */
28104     refreshText : "Refresh",
28105
28106     // private
28107     renderButtons : function(el){
28108         Roo.PagingToolbar.superclass.render.call(this, el);
28109         this.first = this.addButton({
28110             tooltip: this.firstText,
28111             cls: "x-btn-icon x-grid-page-first",
28112             disabled: true,
28113             handler: this.onClick.createDelegate(this, ["first"])
28114         });
28115         this.prev = this.addButton({
28116             tooltip: this.prevText,
28117             cls: "x-btn-icon x-grid-page-prev",
28118             disabled: true,
28119             handler: this.onClick.createDelegate(this, ["prev"])
28120         });
28121         //this.addSeparator();
28122         this.add(this.beforePageText);
28123         this.field = Roo.get(this.addDom({
28124            tag: "input",
28125            type: "text",
28126            size: "3",
28127            value: "1",
28128            cls: "x-grid-page-number"
28129         }).el);
28130         this.field.on("keydown", this.onPagingKeydown, this);
28131         this.field.on("focus", function(){this.dom.select();});
28132         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28133         this.field.setHeight(18);
28134         //this.addSeparator();
28135         this.next = this.addButton({
28136             tooltip: this.nextText,
28137             cls: "x-btn-icon x-grid-page-next",
28138             disabled: true,
28139             handler: this.onClick.createDelegate(this, ["next"])
28140         });
28141         this.last = this.addButton({
28142             tooltip: this.lastText,
28143             cls: "x-btn-icon x-grid-page-last",
28144             disabled: true,
28145             handler: this.onClick.createDelegate(this, ["last"])
28146         });
28147         //this.addSeparator();
28148         this.loading = this.addButton({
28149             tooltip: this.refreshText,
28150             cls: "x-btn-icon x-grid-loading",
28151             handler: this.onClick.createDelegate(this, ["refresh"])
28152         });
28153
28154         if(this.displayInfo){
28155             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28156         }
28157     },
28158
28159     // private
28160     updateInfo : function(){
28161         if(this.displayEl){
28162             var count = this.ds.getCount();
28163             var msg = count == 0 ?
28164                 this.emptyMsg :
28165                 String.format(
28166                     this.displayMsg,
28167                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28168                 );
28169             this.displayEl.update(msg);
28170         }
28171     },
28172
28173     // private
28174     onLoad : function(ds, r, o){
28175        this.cursor = o.params ? o.params.start : 0;
28176        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28177
28178        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28179        this.field.dom.value = ap;
28180        this.first.setDisabled(ap == 1);
28181        this.prev.setDisabled(ap == 1);
28182        this.next.setDisabled(ap == ps);
28183        this.last.setDisabled(ap == ps);
28184        this.loading.enable();
28185        this.updateInfo();
28186     },
28187
28188     // private
28189     getPageData : function(){
28190         var total = this.ds.getTotalCount();
28191         return {
28192             total : total,
28193             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28194             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28195         };
28196     },
28197
28198     // private
28199     onLoadError : function(){
28200         this.loading.enable();
28201     },
28202
28203     // private
28204     onPagingKeydown : function(e){
28205         var k = e.getKey();
28206         var d = this.getPageData();
28207         if(k == e.RETURN){
28208             var v = this.field.dom.value, pageNum;
28209             if(!v || isNaN(pageNum = parseInt(v, 10))){
28210                 this.field.dom.value = d.activePage;
28211                 return;
28212             }
28213             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28214             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28215             e.stopEvent();
28216         }
28217         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))
28218         {
28219           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28220           this.field.dom.value = pageNum;
28221           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28222           e.stopEvent();
28223         }
28224         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28225         {
28226           var v = this.field.dom.value, pageNum; 
28227           var increment = (e.shiftKey) ? 10 : 1;
28228           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28229             increment *= -1;
28230           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28231             this.field.dom.value = d.activePage;
28232             return;
28233           }
28234           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28235           {
28236             this.field.dom.value = parseInt(v, 10) + increment;
28237             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28238             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28239           }
28240           e.stopEvent();
28241         }
28242     },
28243
28244     // private
28245     beforeLoad : function(){
28246         if(this.loading){
28247             this.loading.disable();
28248         }
28249     },
28250
28251     // private
28252     onClick : function(which){
28253         var ds = this.ds;
28254         switch(which){
28255             case "first":
28256                 ds.load({params:{start: 0, limit: this.pageSize}});
28257             break;
28258             case "prev":
28259                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28260             break;
28261             case "next":
28262                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28263             break;
28264             case "last":
28265                 var total = ds.getTotalCount();
28266                 var extra = total % this.pageSize;
28267                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28268                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28269             break;
28270             case "refresh":
28271                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28272             break;
28273         }
28274     },
28275
28276     /**
28277      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28278      * @param {Roo.data.Store} store The data store to unbind
28279      */
28280     unbind : function(ds){
28281         ds.un("beforeload", this.beforeLoad, this);
28282         ds.un("load", this.onLoad, this);
28283         ds.un("loadexception", this.onLoadError, this);
28284         ds.un("remove", this.updateInfo, this);
28285         ds.un("add", this.updateInfo, this);
28286         this.ds = undefined;
28287     },
28288
28289     /**
28290      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28291      * @param {Roo.data.Store} store The data store to bind
28292      */
28293     bind : function(ds){
28294         ds.on("beforeload", this.beforeLoad, this);
28295         ds.on("load", this.onLoad, this);
28296         ds.on("loadexception", this.onLoadError, this);
28297         ds.on("remove", this.updateInfo, this);
28298         ds.on("add", this.updateInfo, this);
28299         this.ds = ds;
28300     }
28301 });/*
28302  * Based on:
28303  * Ext JS Library 1.1.1
28304  * Copyright(c) 2006-2007, Ext JS, LLC.
28305  *
28306  * Originally Released Under LGPL - original licence link has changed is not relivant.
28307  *
28308  * Fork - LGPL
28309  * <script type="text/javascript">
28310  */
28311
28312 /**
28313  * @class Roo.Resizable
28314  * @extends Roo.util.Observable
28315  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28316  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28317  * 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
28318  * the element will be wrapped for you automatically.</p>
28319  * <p>Here is the list of valid resize handles:</p>
28320  * <pre>
28321 Value   Description
28322 ------  -------------------
28323  'n'     north
28324  's'     south
28325  'e'     east
28326  'w'     west
28327  'nw'    northwest
28328  'sw'    southwest
28329  'se'    southeast
28330  'ne'    northeast
28331  'hd'    horizontal drag
28332  'all'   all
28333 </pre>
28334  * <p>Here's an example showing the creation of a typical Resizable:</p>
28335  * <pre><code>
28336 var resizer = new Roo.Resizable("element-id", {
28337     handles: 'all',
28338     minWidth: 200,
28339     minHeight: 100,
28340     maxWidth: 500,
28341     maxHeight: 400,
28342     pinned: true
28343 });
28344 resizer.on("resize", myHandler);
28345 </code></pre>
28346  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28347  * resizer.east.setDisplayed(false);</p>
28348  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28349  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28350  * resize operation's new size (defaults to [0, 0])
28351  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28352  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28353  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28354  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28355  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28356  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28357  * @cfg {Number} width The width of the element in pixels (defaults to null)
28358  * @cfg {Number} height The height of the element in pixels (defaults to null)
28359  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28360  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28361  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28362  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28363  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28364  * in favor of the handles config option (defaults to false)
28365  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28366  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28367  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28368  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28369  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28370  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28371  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28372  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28373  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28374  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28375  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28376  * @constructor
28377  * Create a new resizable component
28378  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
28379  * @param {Object} config configuration options
28380   */
28381 Roo.Resizable = function(el, config)
28382 {
28383     this.el = Roo.get(el);
28384
28385     if(config && config.wrap){
28386         config.resizeChild = this.el;
28387         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
28388         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
28389         this.el.setStyle("overflow", "hidden");
28390         this.el.setPositioning(config.resizeChild.getPositioning());
28391         config.resizeChild.clearPositioning();
28392         if(!config.width || !config.height){
28393             var csize = config.resizeChild.getSize();
28394             this.el.setSize(csize.width, csize.height);
28395         }
28396         if(config.pinned && !config.adjustments){
28397             config.adjustments = "auto";
28398         }
28399     }
28400
28401     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
28402     this.proxy.unselectable();
28403     this.proxy.enableDisplayMode('block');
28404
28405     Roo.apply(this, config);
28406
28407     if(this.pinned){
28408         this.disableTrackOver = true;
28409         this.el.addClass("x-resizable-pinned");
28410     }
28411     // if the element isn't positioned, make it relative
28412     var position = this.el.getStyle("position");
28413     if(position != "absolute" && position != "fixed"){
28414         this.el.setStyle("position", "relative");
28415     }
28416     if(!this.handles){ // no handles passed, must be legacy style
28417         this.handles = 's,e,se';
28418         if(this.multiDirectional){
28419             this.handles += ',n,w';
28420         }
28421     }
28422     if(this.handles == "all"){
28423         this.handles = "n s e w ne nw se sw";
28424     }
28425     var hs = this.handles.split(/\s*?[,;]\s*?| /);
28426     var ps = Roo.Resizable.positions;
28427     for(var i = 0, len = hs.length; i < len; i++){
28428         if(hs[i] && ps[hs[i]]){
28429             var pos = ps[hs[i]];
28430             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
28431         }
28432     }
28433     // legacy
28434     this.corner = this.southeast;
28435     
28436     // updateBox = the box can move..
28437     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
28438         this.updateBox = true;
28439     }
28440
28441     this.activeHandle = null;
28442
28443     if(this.resizeChild){
28444         if(typeof this.resizeChild == "boolean"){
28445             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
28446         }else{
28447             this.resizeChild = Roo.get(this.resizeChild, true);
28448         }
28449     }
28450     
28451     if(this.adjustments == "auto"){
28452         var rc = this.resizeChild;
28453         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
28454         if(rc && (hw || hn)){
28455             rc.position("relative");
28456             rc.setLeft(hw ? hw.el.getWidth() : 0);
28457             rc.setTop(hn ? hn.el.getHeight() : 0);
28458         }
28459         this.adjustments = [
28460             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
28461             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
28462         ];
28463     }
28464
28465     if(this.draggable){
28466         this.dd = this.dynamic ?
28467             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
28468         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
28469     }
28470
28471     // public events
28472     this.addEvents({
28473         /**
28474          * @event beforeresize
28475          * Fired before resize is allowed. Set enabled to false to cancel resize.
28476          * @param {Roo.Resizable} this
28477          * @param {Roo.EventObject} e The mousedown event
28478          */
28479         "beforeresize" : true,
28480         /**
28481          * @event resizing
28482          * Fired a resizing.
28483          * @param {Roo.Resizable} this
28484          * @param {Number} x The new x position
28485          * @param {Number} y The new y position
28486          * @param {Number} w The new w width
28487          * @param {Number} h The new h hight
28488          * @param {Roo.EventObject} e The mouseup event
28489          */
28490         "resizing" : true,
28491         /**
28492          * @event resize
28493          * Fired after a resize.
28494          * @param {Roo.Resizable} this
28495          * @param {Number} width The new width
28496          * @param {Number} height The new height
28497          * @param {Roo.EventObject} e The mouseup event
28498          */
28499         "resize" : true
28500     });
28501
28502     if(this.width !== null && this.height !== null){
28503         this.resizeTo(this.width, this.height);
28504     }else{
28505         this.updateChildSize();
28506     }
28507     if(Roo.isIE){
28508         this.el.dom.style.zoom = 1;
28509     }
28510     Roo.Resizable.superclass.constructor.call(this);
28511 };
28512
28513 Roo.extend(Roo.Resizable, Roo.util.Observable, {
28514         resizeChild : false,
28515         adjustments : [0, 0],
28516         minWidth : 5,
28517         minHeight : 5,
28518         maxWidth : 10000,
28519         maxHeight : 10000,
28520         enabled : true,
28521         animate : false,
28522         duration : .35,
28523         dynamic : false,
28524         handles : false,
28525         multiDirectional : false,
28526         disableTrackOver : false,
28527         easing : 'easeOutStrong',
28528         widthIncrement : 0,
28529         heightIncrement : 0,
28530         pinned : false,
28531         width : null,
28532         height : null,
28533         preserveRatio : false,
28534         transparent: false,
28535         minX: 0,
28536         minY: 0,
28537         draggable: false,
28538
28539         /**
28540          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
28541          */
28542         constrainTo: undefined,
28543         /**
28544          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
28545          */
28546         resizeRegion: undefined,
28547
28548
28549     /**
28550      * Perform a manual resize
28551      * @param {Number} width
28552      * @param {Number} height
28553      */
28554     resizeTo : function(width, height){
28555         this.el.setSize(width, height);
28556         this.updateChildSize();
28557         this.fireEvent("resize", this, width, height, null);
28558     },
28559
28560     // private
28561     startSizing : function(e, handle){
28562         this.fireEvent("beforeresize", this, e);
28563         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
28564
28565             if(!this.overlay){
28566                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
28567                 this.overlay.unselectable();
28568                 this.overlay.enableDisplayMode("block");
28569                 this.overlay.on("mousemove", this.onMouseMove, this);
28570                 this.overlay.on("mouseup", this.onMouseUp, this);
28571             }
28572             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
28573
28574             this.resizing = true;
28575             this.startBox = this.el.getBox();
28576             this.startPoint = e.getXY();
28577             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
28578                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
28579
28580             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28581             this.overlay.show();
28582
28583             if(this.constrainTo) {
28584                 var ct = Roo.get(this.constrainTo);
28585                 this.resizeRegion = ct.getRegion().adjust(
28586                     ct.getFrameWidth('t'),
28587                     ct.getFrameWidth('l'),
28588                     -ct.getFrameWidth('b'),
28589                     -ct.getFrameWidth('r')
28590                 );
28591             }
28592
28593             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
28594             this.proxy.show();
28595             this.proxy.setBox(this.startBox);
28596             if(!this.dynamic){
28597                 this.proxy.setStyle('visibility', 'visible');
28598             }
28599         }
28600     },
28601
28602     // private
28603     onMouseDown : function(handle, e){
28604         if(this.enabled){
28605             e.stopEvent();
28606             this.activeHandle = handle;
28607             this.startSizing(e, handle);
28608         }
28609     },
28610
28611     // private
28612     onMouseUp : function(e){
28613         var size = this.resizeElement();
28614         this.resizing = false;
28615         this.handleOut();
28616         this.overlay.hide();
28617         this.proxy.hide();
28618         this.fireEvent("resize", this, size.width, size.height, e);
28619     },
28620
28621     // private
28622     updateChildSize : function(){
28623         
28624         if(this.resizeChild){
28625             var el = this.el;
28626             var child = this.resizeChild;
28627             var adj = this.adjustments;
28628             if(el.dom.offsetWidth){
28629                 var b = el.getSize(true);
28630                 child.setSize(b.width+adj[0], b.height+adj[1]);
28631             }
28632             // Second call here for IE
28633             // The first call enables instant resizing and
28634             // the second call corrects scroll bars if they
28635             // exist
28636             if(Roo.isIE){
28637                 setTimeout(function(){
28638                     if(el.dom.offsetWidth){
28639                         var b = el.getSize(true);
28640                         child.setSize(b.width+adj[0], b.height+adj[1]);
28641                     }
28642                 }, 10);
28643             }
28644         }
28645     },
28646
28647     // private
28648     snap : function(value, inc, min){
28649         if(!inc || !value) return value;
28650         var newValue = value;
28651         var m = value % inc;
28652         if(m > 0){
28653             if(m > (inc/2)){
28654                 newValue = value + (inc-m);
28655             }else{
28656                 newValue = value - m;
28657             }
28658         }
28659         return Math.max(min, newValue);
28660     },
28661
28662     // private
28663     resizeElement : function(){
28664         var box = this.proxy.getBox();
28665         if(this.updateBox){
28666             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28667         }else{
28668             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28669         }
28670         this.updateChildSize();
28671         if(!this.dynamic){
28672             this.proxy.hide();
28673         }
28674         return box;
28675     },
28676
28677     // private
28678     constrain : function(v, diff, m, mx){
28679         if(v - diff < m){
28680             diff = v - m;
28681         }else if(v - diff > mx){
28682             diff = mx - v;
28683         }
28684         return diff;
28685     },
28686
28687     // private
28688     onMouseMove : function(e){
28689         
28690         if(this.enabled){
28691             try{// try catch so if something goes wrong the user doesn't get hung
28692
28693             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28694                 return;
28695             }
28696
28697             //var curXY = this.startPoint;
28698             var curSize = this.curSize || this.startBox;
28699             var x = this.startBox.x, y = this.startBox.y;
28700             var ox = x, oy = y;
28701             var w = curSize.width, h = curSize.height;
28702             var ow = w, oh = h;
28703             var mw = this.minWidth, mh = this.minHeight;
28704             var mxw = this.maxWidth, mxh = this.maxHeight;
28705             var wi = this.widthIncrement;
28706             var hi = this.heightIncrement;
28707
28708             var eventXY = e.getXY();
28709             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28710             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28711
28712             var pos = this.activeHandle.position;
28713
28714             switch(pos){
28715                 case "east":
28716                     w += diffX;
28717                     w = Math.min(Math.max(mw, w), mxw);
28718                     break;
28719              
28720                 case "south":
28721                     h += diffY;
28722                     h = Math.min(Math.max(mh, h), mxh);
28723                     break;
28724                 case "southeast":
28725                     w += diffX;
28726                     h += diffY;
28727                     w = Math.min(Math.max(mw, w), mxw);
28728                     h = Math.min(Math.max(mh, h), mxh);
28729                     break;
28730                 case "north":
28731                     diffY = this.constrain(h, diffY, mh, mxh);
28732                     y += diffY;
28733                     h -= diffY;
28734                     break;
28735                 case "hdrag":
28736                     
28737                     if (wi) {
28738                         var adiffX = Math.abs(diffX);
28739                         var sub = (adiffX % wi); // how much 
28740                         if (sub > (wi/2)) { // far enough to snap
28741                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28742                         } else {
28743                             // remove difference.. 
28744                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28745                         }
28746                     }
28747                     x += diffX;
28748                     x = Math.max(this.minX, x);
28749                     break;
28750                 case "west":
28751                     diffX = this.constrain(w, diffX, mw, mxw);
28752                     x += diffX;
28753                     w -= diffX;
28754                     break;
28755                 case "northeast":
28756                     w += diffX;
28757                     w = Math.min(Math.max(mw, w), mxw);
28758                     diffY = this.constrain(h, diffY, mh, mxh);
28759                     y += diffY;
28760                     h -= diffY;
28761                     break;
28762                 case "northwest":
28763                     diffX = this.constrain(w, diffX, mw, mxw);
28764                     diffY = this.constrain(h, diffY, mh, mxh);
28765                     y += diffY;
28766                     h -= diffY;
28767                     x += diffX;
28768                     w -= diffX;
28769                     break;
28770                case "southwest":
28771                     diffX = this.constrain(w, diffX, mw, mxw);
28772                     h += diffY;
28773                     h = Math.min(Math.max(mh, h), mxh);
28774                     x += diffX;
28775                     w -= diffX;
28776                     break;
28777             }
28778
28779             var sw = this.snap(w, wi, mw);
28780             var sh = this.snap(h, hi, mh);
28781             if(sw != w || sh != h){
28782                 switch(pos){
28783                     case "northeast":
28784                         y -= sh - h;
28785                     break;
28786                     case "north":
28787                         y -= sh - h;
28788                         break;
28789                     case "southwest":
28790                         x -= sw - w;
28791                     break;
28792                     case "west":
28793                         x -= sw - w;
28794                         break;
28795                     case "northwest":
28796                         x -= sw - w;
28797                         y -= sh - h;
28798                     break;
28799                 }
28800                 w = sw;
28801                 h = sh;
28802             }
28803
28804             if(this.preserveRatio){
28805                 switch(pos){
28806                     case "southeast":
28807                     case "east":
28808                         h = oh * (w/ow);
28809                         h = Math.min(Math.max(mh, h), mxh);
28810                         w = ow * (h/oh);
28811                        break;
28812                     case "south":
28813                         w = ow * (h/oh);
28814                         w = Math.min(Math.max(mw, w), mxw);
28815                         h = oh * (w/ow);
28816                         break;
28817                     case "northeast":
28818                         w = ow * (h/oh);
28819                         w = Math.min(Math.max(mw, w), mxw);
28820                         h = oh * (w/ow);
28821                     break;
28822                     case "north":
28823                         var tw = w;
28824                         w = ow * (h/oh);
28825                         w = Math.min(Math.max(mw, w), mxw);
28826                         h = oh * (w/ow);
28827                         x += (tw - w) / 2;
28828                         break;
28829                     case "southwest":
28830                         h = oh * (w/ow);
28831                         h = Math.min(Math.max(mh, h), mxh);
28832                         var tw = w;
28833                         w = ow * (h/oh);
28834                         x += tw - w;
28835                         break;
28836                     case "west":
28837                         var th = h;
28838                         h = oh * (w/ow);
28839                         h = Math.min(Math.max(mh, h), mxh);
28840                         y += (th - h) / 2;
28841                         var tw = w;
28842                         w = ow * (h/oh);
28843                         x += tw - w;
28844                        break;
28845                     case "northwest":
28846                         var tw = w;
28847                         var th = h;
28848                         h = oh * (w/ow);
28849                         h = Math.min(Math.max(mh, h), mxh);
28850                         w = ow * (h/oh);
28851                         y += th - h;
28852                         x += tw - w;
28853                        break;
28854
28855                 }
28856             }
28857             if (pos == 'hdrag') {
28858                 w = ow;
28859             }
28860             this.proxy.setBounds(x, y, w, h);
28861             if(this.dynamic){
28862                 this.resizeElement();
28863             }
28864             }catch(e){}
28865         }
28866         this.fireEvent("resizing", this, x, y, w, h, e);
28867     },
28868
28869     // private
28870     handleOver : function(){
28871         if(this.enabled){
28872             this.el.addClass("x-resizable-over");
28873         }
28874     },
28875
28876     // private
28877     handleOut : function(){
28878         if(!this.resizing){
28879             this.el.removeClass("x-resizable-over");
28880         }
28881     },
28882
28883     /**
28884      * Returns the element this component is bound to.
28885      * @return {Roo.Element}
28886      */
28887     getEl : function(){
28888         return this.el;
28889     },
28890
28891     /**
28892      * Returns the resizeChild element (or null).
28893      * @return {Roo.Element}
28894      */
28895     getResizeChild : function(){
28896         return this.resizeChild;
28897     },
28898     groupHandler : function()
28899     {
28900         
28901     },
28902     /**
28903      * Destroys this resizable. If the element was wrapped and
28904      * removeEl is not true then the element remains.
28905      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28906      */
28907     destroy : function(removeEl){
28908         this.proxy.remove();
28909         if(this.overlay){
28910             this.overlay.removeAllListeners();
28911             this.overlay.remove();
28912         }
28913         var ps = Roo.Resizable.positions;
28914         for(var k in ps){
28915             if(typeof ps[k] != "function" && this[ps[k]]){
28916                 var h = this[ps[k]];
28917                 h.el.removeAllListeners();
28918                 h.el.remove();
28919             }
28920         }
28921         if(removeEl){
28922             this.el.update("");
28923             this.el.remove();
28924         }
28925     }
28926 });
28927
28928 // private
28929 // hash to map config positions to true positions
28930 Roo.Resizable.positions = {
28931     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28932     hd: "hdrag"
28933 };
28934
28935 // private
28936 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28937     if(!this.tpl){
28938         // only initialize the template if resizable is used
28939         var tpl = Roo.DomHelper.createTemplate(
28940             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28941         );
28942         tpl.compile();
28943         Roo.Resizable.Handle.prototype.tpl = tpl;
28944     }
28945     this.position = pos;
28946     this.rz = rz;
28947     // show north drag fro topdra
28948     var handlepos = pos == 'hdrag' ? 'north' : pos;
28949     
28950     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28951     if (pos == 'hdrag') {
28952         this.el.setStyle('cursor', 'pointer');
28953     }
28954     this.el.unselectable();
28955     if(transparent){
28956         this.el.setOpacity(0);
28957     }
28958     this.el.on("mousedown", this.onMouseDown, this);
28959     if(!disableTrackOver){
28960         this.el.on("mouseover", this.onMouseOver, this);
28961         this.el.on("mouseout", this.onMouseOut, this);
28962     }
28963 };
28964
28965 // private
28966 Roo.Resizable.Handle.prototype = {
28967     afterResize : function(rz){
28968         // do nothing
28969     },
28970     // private
28971     onMouseDown : function(e){
28972         this.rz.onMouseDown(this, e);
28973     },
28974     // private
28975     onMouseOver : function(e){
28976         this.rz.handleOver(this, e);
28977     },
28978     // private
28979     onMouseOut : function(e){
28980         this.rz.handleOut(this, e);
28981     }
28982 };/*
28983  * Based on:
28984  * Ext JS Library 1.1.1
28985  * Copyright(c) 2006-2007, Ext JS, LLC.
28986  *
28987  * Originally Released Under LGPL - original licence link has changed is not relivant.
28988  *
28989  * Fork - LGPL
28990  * <script type="text/javascript">
28991  */
28992
28993 /**
28994  * @class Roo.Editor
28995  * @extends Roo.Component
28996  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28997  * @constructor
28998  * Create a new Editor
28999  * @param {Roo.form.Field} field The Field object (or descendant)
29000  * @param {Object} config The config object
29001  */
29002 Roo.Editor = function(field, config){
29003     Roo.Editor.superclass.constructor.call(this, config);
29004     this.field = field;
29005     this.addEvents({
29006         /**
29007              * @event beforestartedit
29008              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29009              * false from the handler of this event.
29010              * @param {Editor} this
29011              * @param {Roo.Element} boundEl The underlying element bound to this editor
29012              * @param {Mixed} value The field value being set
29013              */
29014         "beforestartedit" : true,
29015         /**
29016              * @event startedit
29017              * Fires when this editor is displayed
29018              * @param {Roo.Element} boundEl The underlying element bound to this editor
29019              * @param {Mixed} value The starting field value
29020              */
29021         "startedit" : true,
29022         /**
29023              * @event beforecomplete
29024              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29025              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29026              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29027              * event will not fire since no edit actually occurred.
29028              * @param {Editor} this
29029              * @param {Mixed} value The current field value
29030              * @param {Mixed} startValue The original field value
29031              */
29032         "beforecomplete" : true,
29033         /**
29034              * @event complete
29035              * Fires after editing is complete and any changed value has been written to the underlying field.
29036              * @param {Editor} this
29037              * @param {Mixed} value The current field value
29038              * @param {Mixed} startValue The original field value
29039              */
29040         "complete" : true,
29041         /**
29042          * @event specialkey
29043          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29044          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29045          * @param {Roo.form.Field} this
29046          * @param {Roo.EventObject} e The event object
29047          */
29048         "specialkey" : true
29049     });
29050 };
29051
29052 Roo.extend(Roo.Editor, Roo.Component, {
29053     /**
29054      * @cfg {Boolean/String} autosize
29055      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29056      * or "height" to adopt the height only (defaults to false)
29057      */
29058     /**
29059      * @cfg {Boolean} revertInvalid
29060      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29061      * validation fails (defaults to true)
29062      */
29063     /**
29064      * @cfg {Boolean} ignoreNoChange
29065      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29066      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29067      * will never be ignored.
29068      */
29069     /**
29070      * @cfg {Boolean} hideEl
29071      * False to keep the bound element visible while the editor is displayed (defaults to true)
29072      */
29073     /**
29074      * @cfg {Mixed} value
29075      * The data value of the underlying field (defaults to "")
29076      */
29077     value : "",
29078     /**
29079      * @cfg {String} alignment
29080      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29081      */
29082     alignment: "c-c?",
29083     /**
29084      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29085      * for bottom-right shadow (defaults to "frame")
29086      */
29087     shadow : "frame",
29088     /**
29089      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29090      */
29091     constrain : false,
29092     /**
29093      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29094      */
29095     completeOnEnter : false,
29096     /**
29097      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29098      */
29099     cancelOnEsc : false,
29100     /**
29101      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29102      */
29103     updateEl : false,
29104
29105     // private
29106     onRender : function(ct, position){
29107         this.el = new Roo.Layer({
29108             shadow: this.shadow,
29109             cls: "x-editor",
29110             parentEl : ct,
29111             shim : this.shim,
29112             shadowOffset:4,
29113             id: this.id,
29114             constrain: this.constrain
29115         });
29116         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29117         if(this.field.msgTarget != 'title'){
29118             this.field.msgTarget = 'qtip';
29119         }
29120         this.field.render(this.el);
29121         if(Roo.isGecko){
29122             this.field.el.dom.setAttribute('autocomplete', 'off');
29123         }
29124         this.field.on("specialkey", this.onSpecialKey, this);
29125         if(this.swallowKeys){
29126             this.field.el.swallowEvent(['keydown','keypress']);
29127         }
29128         this.field.show();
29129         this.field.on("blur", this.onBlur, this);
29130         if(this.field.grow){
29131             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29132         }
29133     },
29134
29135     onSpecialKey : function(field, e)
29136     {
29137         //Roo.log('editor onSpecialKey');
29138         if(this.completeOnEnter && e.getKey() == e.ENTER){
29139             e.stopEvent();
29140             this.completeEdit();
29141             return;
29142         }
29143         // do not fire special key otherwise it might hide close the editor...
29144         if(e.getKey() == e.ENTER){    
29145             return;
29146         }
29147         if(this.cancelOnEsc && e.getKey() == e.ESC){
29148             this.cancelEdit();
29149             return;
29150         } 
29151         this.fireEvent('specialkey', field, e);
29152     
29153     },
29154
29155     /**
29156      * Starts the editing process and shows the editor.
29157      * @param {String/HTMLElement/Element} el The element to edit
29158      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29159       * to the innerHTML of el.
29160      */
29161     startEdit : function(el, value){
29162         if(this.editing){
29163             this.completeEdit();
29164         }
29165         this.boundEl = Roo.get(el);
29166         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29167         if(!this.rendered){
29168             this.render(this.parentEl || document.body);
29169         }
29170         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29171             return;
29172         }
29173         this.startValue = v;
29174         this.field.setValue(v);
29175         if(this.autoSize){
29176             var sz = this.boundEl.getSize();
29177             switch(this.autoSize){
29178                 case "width":
29179                 this.setSize(sz.width,  "");
29180                 break;
29181                 case "height":
29182                 this.setSize("",  sz.height);
29183                 break;
29184                 default:
29185                 this.setSize(sz.width,  sz.height);
29186             }
29187         }
29188         this.el.alignTo(this.boundEl, this.alignment);
29189         this.editing = true;
29190         if(Roo.QuickTips){
29191             Roo.QuickTips.disable();
29192         }
29193         this.show();
29194     },
29195
29196     /**
29197      * Sets the height and width of this editor.
29198      * @param {Number} width The new width
29199      * @param {Number} height The new height
29200      */
29201     setSize : function(w, h){
29202         this.field.setSize(w, h);
29203         if(this.el){
29204             this.el.sync();
29205         }
29206     },
29207
29208     /**
29209      * Realigns the editor to the bound field based on the current alignment config value.
29210      */
29211     realign : function(){
29212         this.el.alignTo(this.boundEl, this.alignment);
29213     },
29214
29215     /**
29216      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29217      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29218      */
29219     completeEdit : function(remainVisible){
29220         if(!this.editing){
29221             return;
29222         }
29223         var v = this.getValue();
29224         if(this.revertInvalid !== false && !this.field.isValid()){
29225             v = this.startValue;
29226             this.cancelEdit(true);
29227         }
29228         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29229             this.editing = false;
29230             this.hide();
29231             return;
29232         }
29233         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29234             this.editing = false;
29235             if(this.updateEl && this.boundEl){
29236                 this.boundEl.update(v);
29237             }
29238             if(remainVisible !== true){
29239                 this.hide();
29240             }
29241             this.fireEvent("complete", this, v, this.startValue);
29242         }
29243     },
29244
29245     // private
29246     onShow : function(){
29247         this.el.show();
29248         if(this.hideEl !== false){
29249             this.boundEl.hide();
29250         }
29251         this.field.show();
29252         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29253             this.fixIEFocus = true;
29254             this.deferredFocus.defer(50, this);
29255         }else{
29256             this.field.focus();
29257         }
29258         this.fireEvent("startedit", this.boundEl, this.startValue);
29259     },
29260
29261     deferredFocus : function(){
29262         if(this.editing){
29263             this.field.focus();
29264         }
29265     },
29266
29267     /**
29268      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29269      * reverted to the original starting value.
29270      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29271      * cancel (defaults to false)
29272      */
29273     cancelEdit : function(remainVisible){
29274         if(this.editing){
29275             this.setValue(this.startValue);
29276             if(remainVisible !== true){
29277                 this.hide();
29278             }
29279         }
29280     },
29281
29282     // private
29283     onBlur : function(){
29284         if(this.allowBlur !== true && this.editing){
29285             this.completeEdit();
29286         }
29287     },
29288
29289     // private
29290     onHide : function(){
29291         if(this.editing){
29292             this.completeEdit();
29293             return;
29294         }
29295         this.field.blur();
29296         if(this.field.collapse){
29297             this.field.collapse();
29298         }
29299         this.el.hide();
29300         if(this.hideEl !== false){
29301             this.boundEl.show();
29302         }
29303         if(Roo.QuickTips){
29304             Roo.QuickTips.enable();
29305         }
29306     },
29307
29308     /**
29309      * Sets the data value of the editor
29310      * @param {Mixed} value Any valid value supported by the underlying field
29311      */
29312     setValue : function(v){
29313         this.field.setValue(v);
29314     },
29315
29316     /**
29317      * Gets the data value of the editor
29318      * @return {Mixed} The data value
29319      */
29320     getValue : function(){
29321         return this.field.getValue();
29322     }
29323 });/*
29324  * Based on:
29325  * Ext JS Library 1.1.1
29326  * Copyright(c) 2006-2007, Ext JS, LLC.
29327  *
29328  * Originally Released Under LGPL - original licence link has changed is not relivant.
29329  *
29330  * Fork - LGPL
29331  * <script type="text/javascript">
29332  */
29333  
29334 /**
29335  * @class Roo.BasicDialog
29336  * @extends Roo.util.Observable
29337  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29338  * <pre><code>
29339 var dlg = new Roo.BasicDialog("my-dlg", {
29340     height: 200,
29341     width: 300,
29342     minHeight: 100,
29343     minWidth: 150,
29344     modal: true,
29345     proxyDrag: true,
29346     shadow: true
29347 });
29348 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29349 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29350 dlg.addButton('Cancel', dlg.hide, dlg);
29351 dlg.show();
29352 </code></pre>
29353   <b>A Dialog should always be a direct child of the body element.</b>
29354  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29355  * @cfg {String} title Default text to display in the title bar (defaults to null)
29356  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29357  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29358  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29359  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29360  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29361  * (defaults to null with no animation)
29362  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29363  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29364  * property for valid values (defaults to 'all')
29365  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29366  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29367  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29368  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29369  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29370  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29371  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29372  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29373  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29374  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29375  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29376  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
29377  * draggable = true (defaults to false)
29378  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
29379  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29380  * shadow (defaults to false)
29381  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
29382  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
29383  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
29384  * @cfg {Array} buttons Array of buttons
29385  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
29386  * @constructor
29387  * Create a new BasicDialog.
29388  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
29389  * @param {Object} config Configuration options
29390  */
29391 Roo.BasicDialog = function(el, config){
29392     this.el = Roo.get(el);
29393     var dh = Roo.DomHelper;
29394     if(!this.el && config && config.autoCreate){
29395         if(typeof config.autoCreate == "object"){
29396             if(!config.autoCreate.id){
29397                 config.autoCreate.id = el;
29398             }
29399             this.el = dh.append(document.body,
29400                         config.autoCreate, true);
29401         }else{
29402             this.el = dh.append(document.body,
29403                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
29404         }
29405     }
29406     el = this.el;
29407     el.setDisplayed(true);
29408     el.hide = this.hideAction;
29409     this.id = el.id;
29410     el.addClass("x-dlg");
29411
29412     Roo.apply(this, config);
29413
29414     this.proxy = el.createProxy("x-dlg-proxy");
29415     this.proxy.hide = this.hideAction;
29416     this.proxy.setOpacity(.5);
29417     this.proxy.hide();
29418
29419     if(config.width){
29420         el.setWidth(config.width);
29421     }
29422     if(config.height){
29423         el.setHeight(config.height);
29424     }
29425     this.size = el.getSize();
29426     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
29427         this.xy = [config.x,config.y];
29428     }else{
29429         this.xy = el.getCenterXY(true);
29430     }
29431     /** The header element @type Roo.Element */
29432     this.header = el.child("> .x-dlg-hd");
29433     /** The body element @type Roo.Element */
29434     this.body = el.child("> .x-dlg-bd");
29435     /** The footer element @type Roo.Element */
29436     this.footer = el.child("> .x-dlg-ft");
29437
29438     if(!this.header){
29439         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
29440     }
29441     if(!this.body){
29442         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
29443     }
29444
29445     this.header.unselectable();
29446     if(this.title){
29447         this.header.update(this.title);
29448     }
29449     // this element allows the dialog to be focused for keyboard event
29450     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
29451     this.focusEl.swallowEvent("click", true);
29452
29453     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
29454
29455     // wrap the body and footer for special rendering
29456     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
29457     if(this.footer){
29458         this.bwrap.dom.appendChild(this.footer.dom);
29459     }
29460
29461     this.bg = this.el.createChild({
29462         tag: "div", cls:"x-dlg-bg",
29463         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
29464     });
29465     this.centerBg = this.bg.child("div.x-dlg-bg-center");
29466
29467
29468     if(this.autoScroll !== false && !this.autoTabs){
29469         this.body.setStyle("overflow", "auto");
29470     }
29471
29472     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
29473
29474     if(this.closable !== false){
29475         this.el.addClass("x-dlg-closable");
29476         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
29477         this.close.on("click", this.closeClick, this);
29478         this.close.addClassOnOver("x-dlg-close-over");
29479     }
29480     if(this.collapsible !== false){
29481         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
29482         this.collapseBtn.on("click", this.collapseClick, this);
29483         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
29484         this.header.on("dblclick", this.collapseClick, this);
29485     }
29486     if(this.resizable !== false){
29487         this.el.addClass("x-dlg-resizable");
29488         this.resizer = new Roo.Resizable(el, {
29489             minWidth: this.minWidth || 80,
29490             minHeight:this.minHeight || 80,
29491             handles: this.resizeHandles || "all",
29492             pinned: true
29493         });
29494         this.resizer.on("beforeresize", this.beforeResize, this);
29495         this.resizer.on("resize", this.onResize, this);
29496     }
29497     if(this.draggable !== false){
29498         el.addClass("x-dlg-draggable");
29499         if (!this.proxyDrag) {
29500             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
29501         }
29502         else {
29503             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
29504         }
29505         dd.setHandleElId(this.header.id);
29506         dd.endDrag = this.endMove.createDelegate(this);
29507         dd.startDrag = this.startMove.createDelegate(this);
29508         dd.onDrag = this.onDrag.createDelegate(this);
29509         dd.scroll = false;
29510         this.dd = dd;
29511     }
29512     if(this.modal){
29513         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
29514         this.mask.enableDisplayMode("block");
29515         this.mask.hide();
29516         this.el.addClass("x-dlg-modal");
29517     }
29518     if(this.shadow){
29519         this.shadow = new Roo.Shadow({
29520             mode : typeof this.shadow == "string" ? this.shadow : "sides",
29521             offset : this.shadowOffset
29522         });
29523     }else{
29524         this.shadowOffset = 0;
29525     }
29526     if(Roo.useShims && this.shim !== false){
29527         this.shim = this.el.createShim();
29528         this.shim.hide = this.hideAction;
29529         this.shim.hide();
29530     }else{
29531         this.shim = false;
29532     }
29533     if(this.autoTabs){
29534         this.initTabs();
29535     }
29536     if (this.buttons) { 
29537         var bts= this.buttons;
29538         this.buttons = [];
29539         Roo.each(bts, function(b) {
29540             this.addButton(b);
29541         }, this);
29542     }
29543     
29544     
29545     this.addEvents({
29546         /**
29547          * @event keydown
29548          * Fires when a key is pressed
29549          * @param {Roo.BasicDialog} this
29550          * @param {Roo.EventObject} e
29551          */
29552         "keydown" : true,
29553         /**
29554          * @event move
29555          * Fires when this dialog is moved by the user.
29556          * @param {Roo.BasicDialog} this
29557          * @param {Number} x The new page X
29558          * @param {Number} y The new page Y
29559          */
29560         "move" : true,
29561         /**
29562          * @event resize
29563          * Fires when this dialog is resized by the user.
29564          * @param {Roo.BasicDialog} this
29565          * @param {Number} width The new width
29566          * @param {Number} height The new height
29567          */
29568         "resize" : true,
29569         /**
29570          * @event beforehide
29571          * Fires before this dialog is hidden.
29572          * @param {Roo.BasicDialog} this
29573          */
29574         "beforehide" : true,
29575         /**
29576          * @event hide
29577          * Fires when this dialog is hidden.
29578          * @param {Roo.BasicDialog} this
29579          */
29580         "hide" : true,
29581         /**
29582          * @event beforeshow
29583          * Fires before this dialog is shown.
29584          * @param {Roo.BasicDialog} this
29585          */
29586         "beforeshow" : true,
29587         /**
29588          * @event show
29589          * Fires when this dialog is shown.
29590          * @param {Roo.BasicDialog} this
29591          */
29592         "show" : true
29593     });
29594     el.on("keydown", this.onKeyDown, this);
29595     el.on("mousedown", this.toFront, this);
29596     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
29597     this.el.hide();
29598     Roo.DialogManager.register(this);
29599     Roo.BasicDialog.superclass.constructor.call(this);
29600 };
29601
29602 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29603     shadowOffset: Roo.isIE ? 6 : 5,
29604     minHeight: 80,
29605     minWidth: 200,
29606     minButtonWidth: 75,
29607     defaultButton: null,
29608     buttonAlign: "right",
29609     tabTag: 'div',
29610     firstShow: true,
29611
29612     /**
29613      * Sets the dialog title text
29614      * @param {String} text The title text to display
29615      * @return {Roo.BasicDialog} this
29616      */
29617     setTitle : function(text){
29618         this.header.update(text);
29619         return this;
29620     },
29621
29622     // private
29623     closeClick : function(){
29624         this.hide();
29625     },
29626
29627     // private
29628     collapseClick : function(){
29629         this[this.collapsed ? "expand" : "collapse"]();
29630     },
29631
29632     /**
29633      * Collapses the dialog to its minimized state (only the title bar is visible).
29634      * Equivalent to the user clicking the collapse dialog button.
29635      */
29636     collapse : function(){
29637         if(!this.collapsed){
29638             this.collapsed = true;
29639             this.el.addClass("x-dlg-collapsed");
29640             this.restoreHeight = this.el.getHeight();
29641             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29642         }
29643     },
29644
29645     /**
29646      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29647      * clicking the expand dialog button.
29648      */
29649     expand : function(){
29650         if(this.collapsed){
29651             this.collapsed = false;
29652             this.el.removeClass("x-dlg-collapsed");
29653             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29654         }
29655     },
29656
29657     /**
29658      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29659      * @return {Roo.TabPanel} The tabs component
29660      */
29661     initTabs : function(){
29662         var tabs = this.getTabs();
29663         while(tabs.getTab(0)){
29664             tabs.removeTab(0);
29665         }
29666         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29667             var dom = el.dom;
29668             tabs.addTab(Roo.id(dom), dom.title);
29669             dom.title = "";
29670         });
29671         tabs.activate(0);
29672         return tabs;
29673     },
29674
29675     // private
29676     beforeResize : function(){
29677         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29678     },
29679
29680     // private
29681     onResize : function(){
29682         this.refreshSize();
29683         this.syncBodyHeight();
29684         this.adjustAssets();
29685         this.focus();
29686         this.fireEvent("resize", this, this.size.width, this.size.height);
29687     },
29688
29689     // private
29690     onKeyDown : function(e){
29691         if(this.isVisible()){
29692             this.fireEvent("keydown", this, e);
29693         }
29694     },
29695
29696     /**
29697      * Resizes the dialog.
29698      * @param {Number} width
29699      * @param {Number} height
29700      * @return {Roo.BasicDialog} this
29701      */
29702     resizeTo : function(width, height){
29703         this.el.setSize(width, height);
29704         this.size = {width: width, height: height};
29705         this.syncBodyHeight();
29706         if(this.fixedcenter){
29707             this.center();
29708         }
29709         if(this.isVisible()){
29710             this.constrainXY();
29711             this.adjustAssets();
29712         }
29713         this.fireEvent("resize", this, width, height);
29714         return this;
29715     },
29716
29717
29718     /**
29719      * Resizes the dialog to fit the specified content size.
29720      * @param {Number} width
29721      * @param {Number} height
29722      * @return {Roo.BasicDialog} this
29723      */
29724     setContentSize : function(w, h){
29725         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29726         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29727         //if(!this.el.isBorderBox()){
29728             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29729             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29730         //}
29731         if(this.tabs){
29732             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29733             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29734         }
29735         this.resizeTo(w, h);
29736         return this;
29737     },
29738
29739     /**
29740      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29741      * executed in response to a particular key being pressed while the dialog is active.
29742      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29743      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29744      * @param {Function} fn The function to call
29745      * @param {Object} scope (optional) The scope of the function
29746      * @return {Roo.BasicDialog} this
29747      */
29748     addKeyListener : function(key, fn, scope){
29749         var keyCode, shift, ctrl, alt;
29750         if(typeof key == "object" && !(key instanceof Array)){
29751             keyCode = key["key"];
29752             shift = key["shift"];
29753             ctrl = key["ctrl"];
29754             alt = key["alt"];
29755         }else{
29756             keyCode = key;
29757         }
29758         var handler = function(dlg, e){
29759             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29760                 var k = e.getKey();
29761                 if(keyCode instanceof Array){
29762                     for(var i = 0, len = keyCode.length; i < len; i++){
29763                         if(keyCode[i] == k){
29764                           fn.call(scope || window, dlg, k, e);
29765                           return;
29766                         }
29767                     }
29768                 }else{
29769                     if(k == keyCode){
29770                         fn.call(scope || window, dlg, k, e);
29771                     }
29772                 }
29773             }
29774         };
29775         this.on("keydown", handler);
29776         return this;
29777     },
29778
29779     /**
29780      * Returns the TabPanel component (creates it if it doesn't exist).
29781      * Note: If you wish to simply check for the existence of tabs without creating them,
29782      * check for a null 'tabs' property.
29783      * @return {Roo.TabPanel} The tabs component
29784      */
29785     getTabs : function(){
29786         if(!this.tabs){
29787             this.el.addClass("x-dlg-auto-tabs");
29788             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29789             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29790         }
29791         return this.tabs;
29792     },
29793
29794     /**
29795      * Adds a button to the footer section of the dialog.
29796      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29797      * object or a valid Roo.DomHelper element config
29798      * @param {Function} handler The function called when the button is clicked
29799      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29800      * @return {Roo.Button} The new button
29801      */
29802     addButton : function(config, handler, scope){
29803         var dh = Roo.DomHelper;
29804         if(!this.footer){
29805             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29806         }
29807         if(!this.btnContainer){
29808             var tb = this.footer.createChild({
29809
29810                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29811                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29812             }, null, true);
29813             this.btnContainer = tb.firstChild.firstChild.firstChild;
29814         }
29815         var bconfig = {
29816             handler: handler,
29817             scope: scope,
29818             minWidth: this.minButtonWidth,
29819             hideParent:true
29820         };
29821         if(typeof config == "string"){
29822             bconfig.text = config;
29823         }else{
29824             if(config.tag){
29825                 bconfig.dhconfig = config;
29826             }else{
29827                 Roo.apply(bconfig, config);
29828             }
29829         }
29830         var fc = false;
29831         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29832             bconfig.position = Math.max(0, bconfig.position);
29833             fc = this.btnContainer.childNodes[bconfig.position];
29834         }
29835          
29836         var btn = new Roo.Button(
29837             fc ? 
29838                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29839                 : this.btnContainer.appendChild(document.createElement("td")),
29840             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29841             bconfig
29842         );
29843         this.syncBodyHeight();
29844         if(!this.buttons){
29845             /**
29846              * Array of all the buttons that have been added to this dialog via addButton
29847              * @type Array
29848              */
29849             this.buttons = [];
29850         }
29851         this.buttons.push(btn);
29852         return btn;
29853     },
29854
29855     /**
29856      * Sets the default button to be focused when the dialog is displayed.
29857      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29858      * @return {Roo.BasicDialog} this
29859      */
29860     setDefaultButton : function(btn){
29861         this.defaultButton = btn;
29862         return this;
29863     },
29864
29865     // private
29866     getHeaderFooterHeight : function(safe){
29867         var height = 0;
29868         if(this.header){
29869            height += this.header.getHeight();
29870         }
29871         if(this.footer){
29872            var fm = this.footer.getMargins();
29873             height += (this.footer.getHeight()+fm.top+fm.bottom);
29874         }
29875         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29876         height += this.centerBg.getPadding("tb");
29877         return height;
29878     },
29879
29880     // private
29881     syncBodyHeight : function()
29882     {
29883         var bd = this.body, // the text
29884             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
29885             bw = this.bwrap;
29886         var height = this.size.height - this.getHeaderFooterHeight(false);
29887         bd.setHeight(height-bd.getMargins("tb"));
29888         var hh = this.header.getHeight();
29889         var h = this.size.height-hh;
29890         cb.setHeight(h);
29891         
29892         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29893         bw.setHeight(h-cb.getPadding("tb"));
29894         
29895         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29896         bd.setWidth(bw.getWidth(true));
29897         if(this.tabs){
29898             this.tabs.syncHeight();
29899             if(Roo.isIE){
29900                 this.tabs.el.repaint();
29901             }
29902         }
29903     },
29904
29905     /**
29906      * Restores the previous state of the dialog if Roo.state is configured.
29907      * @return {Roo.BasicDialog} this
29908      */
29909     restoreState : function(){
29910         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29911         if(box && box.width){
29912             this.xy = [box.x, box.y];
29913             this.resizeTo(box.width, box.height);
29914         }
29915         return this;
29916     },
29917
29918     // private
29919     beforeShow : function(){
29920         this.expand();
29921         if(this.fixedcenter){
29922             this.xy = this.el.getCenterXY(true);
29923         }
29924         if(this.modal){
29925             Roo.get(document.body).addClass("x-body-masked");
29926             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29927             this.mask.show();
29928         }
29929         this.constrainXY();
29930     },
29931
29932     // private
29933     animShow : function(){
29934         var b = Roo.get(this.animateTarget).getBox();
29935         this.proxy.setSize(b.width, b.height);
29936         this.proxy.setLocation(b.x, b.y);
29937         this.proxy.show();
29938         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29939                     true, .35, this.showEl.createDelegate(this));
29940     },
29941
29942     /**
29943      * Shows the dialog.
29944      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29945      * @return {Roo.BasicDialog} this
29946      */
29947     show : function(animateTarget){
29948         if (this.fireEvent("beforeshow", this) === false){
29949             return;
29950         }
29951         if(this.syncHeightBeforeShow){
29952             this.syncBodyHeight();
29953         }else if(this.firstShow){
29954             this.firstShow = false;
29955             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29956         }
29957         this.animateTarget = animateTarget || this.animateTarget;
29958         if(!this.el.isVisible()){
29959             this.beforeShow();
29960             if(this.animateTarget && Roo.get(this.animateTarget)){
29961                 this.animShow();
29962             }else{
29963                 this.showEl();
29964             }
29965         }
29966         return this;
29967     },
29968
29969     // private
29970     showEl : function(){
29971         this.proxy.hide();
29972         this.el.setXY(this.xy);
29973         this.el.show();
29974         this.adjustAssets(true);
29975         this.toFront();
29976         this.focus();
29977         // IE peekaboo bug - fix found by Dave Fenwick
29978         if(Roo.isIE){
29979             this.el.repaint();
29980         }
29981         this.fireEvent("show", this);
29982     },
29983
29984     /**
29985      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29986      * dialog itself will receive focus.
29987      */
29988     focus : function(){
29989         if(this.defaultButton){
29990             this.defaultButton.focus();
29991         }else{
29992             this.focusEl.focus();
29993         }
29994     },
29995
29996     // private
29997     constrainXY : function(){
29998         if(this.constraintoviewport !== false){
29999             if(!this.viewSize){
30000                 if(this.container){
30001                     var s = this.container.getSize();
30002                     this.viewSize = [s.width, s.height];
30003                 }else{
30004                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30005                 }
30006             }
30007             var s = Roo.get(this.container||document).getScroll();
30008
30009             var x = this.xy[0], y = this.xy[1];
30010             var w = this.size.width, h = this.size.height;
30011             var vw = this.viewSize[0], vh = this.viewSize[1];
30012             // only move it if it needs it
30013             var moved = false;
30014             // first validate right/bottom
30015             if(x + w > vw+s.left){
30016                 x = vw - w;
30017                 moved = true;
30018             }
30019             if(y + h > vh+s.top){
30020                 y = vh - h;
30021                 moved = true;
30022             }
30023             // then make sure top/left isn't negative
30024             if(x < s.left){
30025                 x = s.left;
30026                 moved = true;
30027             }
30028             if(y < s.top){
30029                 y = s.top;
30030                 moved = true;
30031             }
30032             if(moved){
30033                 // cache xy
30034                 this.xy = [x, y];
30035                 if(this.isVisible()){
30036                     this.el.setLocation(x, y);
30037                     this.adjustAssets();
30038                 }
30039             }
30040         }
30041     },
30042
30043     // private
30044     onDrag : function(){
30045         if(!this.proxyDrag){
30046             this.xy = this.el.getXY();
30047             this.adjustAssets();
30048         }
30049     },
30050
30051     // private
30052     adjustAssets : function(doShow){
30053         var x = this.xy[0], y = this.xy[1];
30054         var w = this.size.width, h = this.size.height;
30055         if(doShow === true){
30056             if(this.shadow){
30057                 this.shadow.show(this.el);
30058             }
30059             if(this.shim){
30060                 this.shim.show();
30061             }
30062         }
30063         if(this.shadow && this.shadow.isVisible()){
30064             this.shadow.show(this.el);
30065         }
30066         if(this.shim && this.shim.isVisible()){
30067             this.shim.setBounds(x, y, w, h);
30068         }
30069     },
30070
30071     // private
30072     adjustViewport : function(w, h){
30073         if(!w || !h){
30074             w = Roo.lib.Dom.getViewWidth();
30075             h = Roo.lib.Dom.getViewHeight();
30076         }
30077         // cache the size
30078         this.viewSize = [w, h];
30079         if(this.modal && this.mask.isVisible()){
30080             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30081             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30082         }
30083         if(this.isVisible()){
30084             this.constrainXY();
30085         }
30086     },
30087
30088     /**
30089      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30090      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30091      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30092      */
30093     destroy : function(removeEl){
30094         if(this.isVisible()){
30095             this.animateTarget = null;
30096             this.hide();
30097         }
30098         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30099         if(this.tabs){
30100             this.tabs.destroy(removeEl);
30101         }
30102         Roo.destroy(
30103              this.shim,
30104              this.proxy,
30105              this.resizer,
30106              this.close,
30107              this.mask
30108         );
30109         if(this.dd){
30110             this.dd.unreg();
30111         }
30112         if(this.buttons){
30113            for(var i = 0, len = this.buttons.length; i < len; i++){
30114                this.buttons[i].destroy();
30115            }
30116         }
30117         this.el.removeAllListeners();
30118         if(removeEl === true){
30119             this.el.update("");
30120             this.el.remove();
30121         }
30122         Roo.DialogManager.unregister(this);
30123     },
30124
30125     // private
30126     startMove : function(){
30127         if(this.proxyDrag){
30128             this.proxy.show();
30129         }
30130         if(this.constraintoviewport !== false){
30131             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30132         }
30133     },
30134
30135     // private
30136     endMove : function(){
30137         if(!this.proxyDrag){
30138             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30139         }else{
30140             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30141             this.proxy.hide();
30142         }
30143         this.refreshSize();
30144         this.adjustAssets();
30145         this.focus();
30146         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30147     },
30148
30149     /**
30150      * Brings this dialog to the front of any other visible dialogs
30151      * @return {Roo.BasicDialog} this
30152      */
30153     toFront : function(){
30154         Roo.DialogManager.bringToFront(this);
30155         return this;
30156     },
30157
30158     /**
30159      * Sends this dialog to the back (under) of any other visible dialogs
30160      * @return {Roo.BasicDialog} this
30161      */
30162     toBack : function(){
30163         Roo.DialogManager.sendToBack(this);
30164         return this;
30165     },
30166
30167     /**
30168      * Centers this dialog in the viewport
30169      * @return {Roo.BasicDialog} this
30170      */
30171     center : function(){
30172         var xy = this.el.getCenterXY(true);
30173         this.moveTo(xy[0], xy[1]);
30174         return this;
30175     },
30176
30177     /**
30178      * Moves the dialog's top-left corner to the specified point
30179      * @param {Number} x
30180      * @param {Number} y
30181      * @return {Roo.BasicDialog} this
30182      */
30183     moveTo : function(x, y){
30184         this.xy = [x,y];
30185         if(this.isVisible()){
30186             this.el.setXY(this.xy);
30187             this.adjustAssets();
30188         }
30189         return this;
30190     },
30191
30192     /**
30193      * Aligns the dialog to the specified element
30194      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30195      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30196      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30197      * @return {Roo.BasicDialog} this
30198      */
30199     alignTo : function(element, position, offsets){
30200         this.xy = this.el.getAlignToXY(element, position, offsets);
30201         if(this.isVisible()){
30202             this.el.setXY(this.xy);
30203             this.adjustAssets();
30204         }
30205         return this;
30206     },
30207
30208     /**
30209      * Anchors an element to another element and realigns it when the window is resized.
30210      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30211      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30212      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30213      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30214      * is a number, it is used as the buffer delay (defaults to 50ms).
30215      * @return {Roo.BasicDialog} this
30216      */
30217     anchorTo : function(el, alignment, offsets, monitorScroll){
30218         var action = function(){
30219             this.alignTo(el, alignment, offsets);
30220         };
30221         Roo.EventManager.onWindowResize(action, this);
30222         var tm = typeof monitorScroll;
30223         if(tm != 'undefined'){
30224             Roo.EventManager.on(window, 'scroll', action, this,
30225                 {buffer: tm == 'number' ? monitorScroll : 50});
30226         }
30227         action.call(this);
30228         return this;
30229     },
30230
30231     /**
30232      * Returns true if the dialog is visible
30233      * @return {Boolean}
30234      */
30235     isVisible : function(){
30236         return this.el.isVisible();
30237     },
30238
30239     // private
30240     animHide : function(callback){
30241         var b = Roo.get(this.animateTarget).getBox();
30242         this.proxy.show();
30243         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30244         this.el.hide();
30245         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30246                     this.hideEl.createDelegate(this, [callback]));
30247     },
30248
30249     /**
30250      * Hides the dialog.
30251      * @param {Function} callback (optional) Function to call when the dialog is hidden
30252      * @return {Roo.BasicDialog} this
30253      */
30254     hide : function(callback){
30255         if (this.fireEvent("beforehide", this) === false){
30256             return;
30257         }
30258         if(this.shadow){
30259             this.shadow.hide();
30260         }
30261         if(this.shim) {
30262           this.shim.hide();
30263         }
30264         // sometimes animateTarget seems to get set.. causing problems...
30265         // this just double checks..
30266         if(this.animateTarget && Roo.get(this.animateTarget)) {
30267            this.animHide(callback);
30268         }else{
30269             this.el.hide();
30270             this.hideEl(callback);
30271         }
30272         return this;
30273     },
30274
30275     // private
30276     hideEl : function(callback){
30277         this.proxy.hide();
30278         if(this.modal){
30279             this.mask.hide();
30280             Roo.get(document.body).removeClass("x-body-masked");
30281         }
30282         this.fireEvent("hide", this);
30283         if(typeof callback == "function"){
30284             callback();
30285         }
30286     },
30287
30288     // private
30289     hideAction : function(){
30290         this.setLeft("-10000px");
30291         this.setTop("-10000px");
30292         this.setStyle("visibility", "hidden");
30293     },
30294
30295     // private
30296     refreshSize : function(){
30297         this.size = this.el.getSize();
30298         this.xy = this.el.getXY();
30299         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30300     },
30301
30302     // private
30303     // z-index is managed by the DialogManager and may be overwritten at any time
30304     setZIndex : function(index){
30305         if(this.modal){
30306             this.mask.setStyle("z-index", index);
30307         }
30308         if(this.shim){
30309             this.shim.setStyle("z-index", ++index);
30310         }
30311         if(this.shadow){
30312             this.shadow.setZIndex(++index);
30313         }
30314         this.el.setStyle("z-index", ++index);
30315         if(this.proxy){
30316             this.proxy.setStyle("z-index", ++index);
30317         }
30318         if(this.resizer){
30319             this.resizer.proxy.setStyle("z-index", ++index);
30320         }
30321
30322         this.lastZIndex = index;
30323     },
30324
30325     /**
30326      * Returns the element for this dialog
30327      * @return {Roo.Element} The underlying dialog Element
30328      */
30329     getEl : function(){
30330         return this.el;
30331     }
30332 });
30333
30334 /**
30335  * @class Roo.DialogManager
30336  * Provides global access to BasicDialogs that have been created and
30337  * support for z-indexing (layering) multiple open dialogs.
30338  */
30339 Roo.DialogManager = function(){
30340     var list = {};
30341     var accessList = [];
30342     var front = null;
30343
30344     // private
30345     var sortDialogs = function(d1, d2){
30346         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30347     };
30348
30349     // private
30350     var orderDialogs = function(){
30351         accessList.sort(sortDialogs);
30352         var seed = Roo.DialogManager.zseed;
30353         for(var i = 0, len = accessList.length; i < len; i++){
30354             var dlg = accessList[i];
30355             if(dlg){
30356                 dlg.setZIndex(seed + (i*10));
30357             }
30358         }
30359     };
30360
30361     return {
30362         /**
30363          * The starting z-index for BasicDialogs (defaults to 9000)
30364          * @type Number The z-index value
30365          */
30366         zseed : 9000,
30367
30368         // private
30369         register : function(dlg){
30370             list[dlg.id] = dlg;
30371             accessList.push(dlg);
30372         },
30373
30374         // private
30375         unregister : function(dlg){
30376             delete list[dlg.id];
30377             var i=0;
30378             var len=0;
30379             if(!accessList.indexOf){
30380                 for(  i = 0, len = accessList.length; i < len; i++){
30381                     if(accessList[i] == dlg){
30382                         accessList.splice(i, 1);
30383                         return;
30384                     }
30385                 }
30386             }else{
30387                  i = accessList.indexOf(dlg);
30388                 if(i != -1){
30389                     accessList.splice(i, 1);
30390                 }
30391             }
30392         },
30393
30394         /**
30395          * Gets a registered dialog by id
30396          * @param {String/Object} id The id of the dialog or a dialog
30397          * @return {Roo.BasicDialog} this
30398          */
30399         get : function(id){
30400             return typeof id == "object" ? id : list[id];
30401         },
30402
30403         /**
30404          * Brings the specified dialog to the front
30405          * @param {String/Object} dlg The id of the dialog or a dialog
30406          * @return {Roo.BasicDialog} this
30407          */
30408         bringToFront : function(dlg){
30409             dlg = this.get(dlg);
30410             if(dlg != front){
30411                 front = dlg;
30412                 dlg._lastAccess = new Date().getTime();
30413                 orderDialogs();
30414             }
30415             return dlg;
30416         },
30417
30418         /**
30419          * Sends the specified dialog to the back
30420          * @param {String/Object} dlg The id of the dialog or a dialog
30421          * @return {Roo.BasicDialog} this
30422          */
30423         sendToBack : function(dlg){
30424             dlg = this.get(dlg);
30425             dlg._lastAccess = -(new Date().getTime());
30426             orderDialogs();
30427             return dlg;
30428         },
30429
30430         /**
30431          * Hides all dialogs
30432          */
30433         hideAll : function(){
30434             for(var id in list){
30435                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
30436                     list[id].hide();
30437                 }
30438             }
30439         }
30440     };
30441 }();
30442
30443 /**
30444  * @class Roo.LayoutDialog
30445  * @extends Roo.BasicDialog
30446  * Dialog which provides adjustments for working with a layout in a Dialog.
30447  * Add your necessary layout config options to the dialog's config.<br>
30448  * Example usage (including a nested layout):
30449  * <pre><code>
30450 if(!dialog){
30451     dialog = new Roo.LayoutDialog("download-dlg", {
30452         modal: true,
30453         width:600,
30454         height:450,
30455         shadow:true,
30456         minWidth:500,
30457         minHeight:350,
30458         autoTabs:true,
30459         proxyDrag:true,
30460         // layout config merges with the dialog config
30461         center:{
30462             tabPosition: "top",
30463             alwaysShowTabs: true
30464         }
30465     });
30466     dialog.addKeyListener(27, dialog.hide, dialog);
30467     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
30468     dialog.addButton("Build It!", this.getDownload, this);
30469
30470     // we can even add nested layouts
30471     var innerLayout = new Roo.BorderLayout("dl-inner", {
30472         east: {
30473             initialSize: 200,
30474             autoScroll:true,
30475             split:true
30476         },
30477         center: {
30478             autoScroll:true
30479         }
30480     });
30481     innerLayout.beginUpdate();
30482     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
30483     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
30484     innerLayout.endUpdate(true);
30485
30486     var layout = dialog.getLayout();
30487     layout.beginUpdate();
30488     layout.add("center", new Roo.ContentPanel("standard-panel",
30489                         {title: "Download the Source", fitToFrame:true}));
30490     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
30491                {title: "Build your own roo.js"}));
30492     layout.getRegion("center").showPanel(sp);
30493     layout.endUpdate();
30494 }
30495 </code></pre>
30496     * @constructor
30497     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
30498     * @param {Object} config configuration options
30499   */
30500 Roo.LayoutDialog = function(el, cfg){
30501     
30502     var config=  cfg;
30503     if (typeof(cfg) == 'undefined') {
30504         config = Roo.apply({}, el);
30505         // not sure why we use documentElement here.. - it should always be body.
30506         // IE7 borks horribly if we use documentElement.
30507         // webkit also does not like documentElement - it creates a body element...
30508         el = Roo.get( document.body || document.documentElement ).createChild();
30509         //config.autoCreate = true;
30510     }
30511     
30512     
30513     config.autoTabs = false;
30514     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
30515     this.body.setStyle({overflow:"hidden", position:"relative"});
30516     this.layout = new Roo.BorderLayout(this.body.dom, config);
30517     this.layout.monitorWindowResize = false;
30518     this.el.addClass("x-dlg-auto-layout");
30519     // fix case when center region overwrites center function
30520     this.center = Roo.BasicDialog.prototype.center;
30521     this.on("show", this.layout.layout, this.layout, true);
30522     if (config.items) {
30523         var xitems = config.items;
30524         delete config.items;
30525         Roo.each(xitems, this.addxtype, this);
30526     }
30527     
30528     
30529 };
30530 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
30531     /**
30532      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
30533      * @deprecated
30534      */
30535     endUpdate : function(){
30536         this.layout.endUpdate();
30537     },
30538
30539     /**
30540      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
30541      *  @deprecated
30542      */
30543     beginUpdate : function(){
30544         this.layout.beginUpdate();
30545     },
30546
30547     /**
30548      * Get the BorderLayout for this dialog
30549      * @return {Roo.BorderLayout}
30550      */
30551     getLayout : function(){
30552         return this.layout;
30553     },
30554
30555     showEl : function(){
30556         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
30557         if(Roo.isIE7){
30558             this.layout.layout();
30559         }
30560     },
30561
30562     // private
30563     // Use the syncHeightBeforeShow config option to control this automatically
30564     syncBodyHeight : function(){
30565         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
30566         if(this.layout){this.layout.layout();}
30567     },
30568     
30569       /**
30570      * Add an xtype element (actually adds to the layout.)
30571      * @return {Object} xdata xtype object data.
30572      */
30573     
30574     addxtype : function(c) {
30575         return this.layout.addxtype(c);
30576     }
30577 });/*
30578  * Based on:
30579  * Ext JS Library 1.1.1
30580  * Copyright(c) 2006-2007, Ext JS, LLC.
30581  *
30582  * Originally Released Under LGPL - original licence link has changed is not relivant.
30583  *
30584  * Fork - LGPL
30585  * <script type="text/javascript">
30586  */
30587  
30588 /**
30589  * @class Roo.MessageBox
30590  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
30591  * Example usage:
30592  *<pre><code>
30593 // Basic alert:
30594 Roo.Msg.alert('Status', 'Changes saved successfully.');
30595
30596 // Prompt for user data:
30597 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
30598     if (btn == 'ok'){
30599         // process text value...
30600     }
30601 });
30602
30603 // Show a dialog using config options:
30604 Roo.Msg.show({
30605    title:'Save Changes?',
30606    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
30607    buttons: Roo.Msg.YESNOCANCEL,
30608    fn: processResult,
30609    animEl: 'elId'
30610 });
30611 </code></pre>
30612  * @singleton
30613  */
30614 Roo.MessageBox = function(){
30615     var dlg, opt, mask, waitTimer;
30616     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30617     var buttons, activeTextEl, bwidth;
30618
30619     // private
30620     var handleButton = function(button){
30621         dlg.hide();
30622         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30623     };
30624
30625     // private
30626     var handleHide = function(){
30627         if(opt && opt.cls){
30628             dlg.el.removeClass(opt.cls);
30629         }
30630         if(waitTimer){
30631             Roo.TaskMgr.stop(waitTimer);
30632             waitTimer = null;
30633         }
30634     };
30635
30636     // private
30637     var updateButtons = function(b){
30638         var width = 0;
30639         if(!b){
30640             buttons["ok"].hide();
30641             buttons["cancel"].hide();
30642             buttons["yes"].hide();
30643             buttons["no"].hide();
30644             dlg.footer.dom.style.display = 'none';
30645             return width;
30646         }
30647         dlg.footer.dom.style.display = '';
30648         for(var k in buttons){
30649             if(typeof buttons[k] != "function"){
30650                 if(b[k]){
30651                     buttons[k].show();
30652                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30653                     width += buttons[k].el.getWidth()+15;
30654                 }else{
30655                     buttons[k].hide();
30656                 }
30657             }
30658         }
30659         return width;
30660     };
30661
30662     // private
30663     var handleEsc = function(d, k, e){
30664         if(opt && opt.closable !== false){
30665             dlg.hide();
30666         }
30667         if(e){
30668             e.stopEvent();
30669         }
30670     };
30671
30672     return {
30673         /**
30674          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30675          * @return {Roo.BasicDialog} The BasicDialog element
30676          */
30677         getDialog : function(){
30678            if(!dlg){
30679                 dlg = new Roo.BasicDialog("x-msg-box", {
30680                     autoCreate : true,
30681                     shadow: true,
30682                     draggable: true,
30683                     resizable:false,
30684                     constraintoviewport:false,
30685                     fixedcenter:true,
30686                     collapsible : false,
30687                     shim:true,
30688                     modal: true,
30689                     width:400, height:100,
30690                     buttonAlign:"center",
30691                     closeClick : function(){
30692                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30693                             handleButton("no");
30694                         }else{
30695                             handleButton("cancel");
30696                         }
30697                     }
30698                 });
30699                 dlg.on("hide", handleHide);
30700                 mask = dlg.mask;
30701                 dlg.addKeyListener(27, handleEsc);
30702                 buttons = {};
30703                 var bt = this.buttonText;
30704                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30705                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30706                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30707                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30708                 bodyEl = dlg.body.createChild({
30709
30710                     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>'
30711                 });
30712                 msgEl = bodyEl.dom.firstChild;
30713                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30714                 textboxEl.enableDisplayMode();
30715                 textboxEl.addKeyListener([10,13], function(){
30716                     if(dlg.isVisible() && opt && opt.buttons){
30717                         if(opt.buttons.ok){
30718                             handleButton("ok");
30719                         }else if(opt.buttons.yes){
30720                             handleButton("yes");
30721                         }
30722                     }
30723                 });
30724                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30725                 textareaEl.enableDisplayMode();
30726                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30727                 progressEl.enableDisplayMode();
30728                 var pf = progressEl.dom.firstChild;
30729                 if (pf) {
30730                     pp = Roo.get(pf.firstChild);
30731                     pp.setHeight(pf.offsetHeight);
30732                 }
30733                 
30734             }
30735             return dlg;
30736         },
30737
30738         /**
30739          * Updates the message box body text
30740          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30741          * the XHTML-compliant non-breaking space character '&amp;#160;')
30742          * @return {Roo.MessageBox} This message box
30743          */
30744         updateText : function(text){
30745             if(!dlg.isVisible() && !opt.width){
30746                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30747             }
30748             msgEl.innerHTML = text || '&#160;';
30749       
30750             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30751             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30752             var w = Math.max(
30753                     Math.min(opt.width || cw , this.maxWidth), 
30754                     Math.max(opt.minWidth || this.minWidth, bwidth)
30755             );
30756             if(opt.prompt){
30757                 activeTextEl.setWidth(w);
30758             }
30759             if(dlg.isVisible()){
30760                 dlg.fixedcenter = false;
30761             }
30762             // to big, make it scroll. = But as usual stupid IE does not support
30763             // !important..
30764             
30765             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30766                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30767                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30768             } else {
30769                 bodyEl.dom.style.height = '';
30770                 bodyEl.dom.style.overflowY = '';
30771             }
30772             if (cw > w) {
30773                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30774             } else {
30775                 bodyEl.dom.style.overflowX = '';
30776             }
30777             
30778             dlg.setContentSize(w, bodyEl.getHeight());
30779             if(dlg.isVisible()){
30780                 dlg.fixedcenter = true;
30781             }
30782             return this;
30783         },
30784
30785         /**
30786          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30787          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30788          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30789          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30790          * @return {Roo.MessageBox} This message box
30791          */
30792         updateProgress : function(value, text){
30793             if(text){
30794                 this.updateText(text);
30795             }
30796             if (pp) { // weird bug on my firefox - for some reason this is not defined
30797                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30798             }
30799             return this;
30800         },        
30801
30802         /**
30803          * Returns true if the message box is currently displayed
30804          * @return {Boolean} True if the message box is visible, else false
30805          */
30806         isVisible : function(){
30807             return dlg && dlg.isVisible();  
30808         },
30809
30810         /**
30811          * Hides the message box if it is displayed
30812          */
30813         hide : function(){
30814             if(this.isVisible()){
30815                 dlg.hide();
30816             }  
30817         },
30818
30819         /**
30820          * Displays a new message box, or reinitializes an existing message box, based on the config options
30821          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30822          * The following config object properties are supported:
30823          * <pre>
30824 Property    Type             Description
30825 ----------  ---------------  ------------------------------------------------------------------------------------
30826 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30827                                    closes (defaults to undefined)
30828 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30829                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30830 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30831                                    progress and wait dialogs will ignore this property and always hide the
30832                                    close button as they can only be closed programmatically.
30833 cls               String           A custom CSS class to apply to the message box element
30834 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30835                                    displayed (defaults to 75)
30836 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30837                                    function will be btn (the name of the button that was clicked, if applicable,
30838                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30839                                    Progress and wait dialogs will ignore this option since they do not respond to
30840                                    user actions and can only be closed programmatically, so any required function
30841                                    should be called by the same code after it closes the dialog.
30842 icon              String           A CSS class that provides a background image to be used as an icon for
30843                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30844 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30845 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30846 modal             Boolean          False to allow user interaction with the page while the message box is
30847                                    displayed (defaults to true)
30848 msg               String           A string that will replace the existing message box body text (defaults
30849                                    to the XHTML-compliant non-breaking space character '&#160;')
30850 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30851 progress          Boolean          True to display a progress bar (defaults to false)
30852 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30853 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30854 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30855 title             String           The title text
30856 value             String           The string value to set into the active textbox element if displayed
30857 wait              Boolean          True to display a progress bar (defaults to false)
30858 width             Number           The width of the dialog in pixels
30859 </pre>
30860          *
30861          * Example usage:
30862          * <pre><code>
30863 Roo.Msg.show({
30864    title: 'Address',
30865    msg: 'Please enter your address:',
30866    width: 300,
30867    buttons: Roo.MessageBox.OKCANCEL,
30868    multiline: true,
30869    fn: saveAddress,
30870    animEl: 'addAddressBtn'
30871 });
30872 </code></pre>
30873          * @param {Object} config Configuration options
30874          * @return {Roo.MessageBox} This message box
30875          */
30876         show : function(options)
30877         {
30878             
30879             // this causes nightmares if you show one dialog after another
30880             // especially on callbacks..
30881              
30882             if(this.isVisible()){
30883                 
30884                 this.hide();
30885                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
30886                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
30887                 Roo.log("New Dialog Message:" +  options.msg )
30888                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30889                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30890                 
30891             }
30892             var d = this.getDialog();
30893             opt = options;
30894             d.setTitle(opt.title || "&#160;");
30895             d.close.setDisplayed(opt.closable !== false);
30896             activeTextEl = textboxEl;
30897             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30898             if(opt.prompt){
30899                 if(opt.multiline){
30900                     textboxEl.hide();
30901                     textareaEl.show();
30902                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30903                         opt.multiline : this.defaultTextHeight);
30904                     activeTextEl = textareaEl;
30905                 }else{
30906                     textboxEl.show();
30907                     textareaEl.hide();
30908                 }
30909             }else{
30910                 textboxEl.hide();
30911                 textareaEl.hide();
30912             }
30913             progressEl.setDisplayed(opt.progress === true);
30914             this.updateProgress(0);
30915             activeTextEl.dom.value = opt.value || "";
30916             if(opt.prompt){
30917                 dlg.setDefaultButton(activeTextEl);
30918             }else{
30919                 var bs = opt.buttons;
30920                 var db = null;
30921                 if(bs && bs.ok){
30922                     db = buttons["ok"];
30923                 }else if(bs && bs.yes){
30924                     db = buttons["yes"];
30925                 }
30926                 dlg.setDefaultButton(db);
30927             }
30928             bwidth = updateButtons(opt.buttons);
30929             this.updateText(opt.msg);
30930             if(opt.cls){
30931                 d.el.addClass(opt.cls);
30932             }
30933             d.proxyDrag = opt.proxyDrag === true;
30934             d.modal = opt.modal !== false;
30935             d.mask = opt.modal !== false ? mask : false;
30936             if(!d.isVisible()){
30937                 // force it to the end of the z-index stack so it gets a cursor in FF
30938                 document.body.appendChild(dlg.el.dom);
30939                 d.animateTarget = null;
30940                 d.show(options.animEl);
30941             }
30942             return this;
30943         },
30944
30945         /**
30946          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30947          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30948          * and closing the message box when the process is complete.
30949          * @param {String} title The title bar text
30950          * @param {String} msg The message box body text
30951          * @return {Roo.MessageBox} This message box
30952          */
30953         progress : function(title, msg){
30954             this.show({
30955                 title : title,
30956                 msg : msg,
30957                 buttons: false,
30958                 progress:true,
30959                 closable:false,
30960                 minWidth: this.minProgressWidth,
30961                 modal : true
30962             });
30963             return this;
30964         },
30965
30966         /**
30967          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30968          * If a callback function is passed it will be called after the user clicks the button, and the
30969          * id of the button that was clicked will be passed as the only parameter to the callback
30970          * (could also be the top-right close button).
30971          * @param {String} title The title bar text
30972          * @param {String} msg The message box body text
30973          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30974          * @param {Object} scope (optional) The scope of the callback function
30975          * @return {Roo.MessageBox} This message box
30976          */
30977         alert : function(title, msg, fn, scope){
30978             this.show({
30979                 title : title,
30980                 msg : msg,
30981                 buttons: this.OK,
30982                 fn: fn,
30983                 scope : scope,
30984                 modal : true
30985             });
30986             return this;
30987         },
30988
30989         /**
30990          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30991          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30992          * You are responsible for closing the message box when the process is complete.
30993          * @param {String} msg The message box body text
30994          * @param {String} title (optional) The title bar text
30995          * @return {Roo.MessageBox} This message box
30996          */
30997         wait : function(msg, title){
30998             this.show({
30999                 title : title,
31000                 msg : msg,
31001                 buttons: false,
31002                 closable:false,
31003                 progress:true,
31004                 modal:true,
31005                 width:300,
31006                 wait:true
31007             });
31008             waitTimer = Roo.TaskMgr.start({
31009                 run: function(i){
31010                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31011                 },
31012                 interval: 1000
31013             });
31014             return this;
31015         },
31016
31017         /**
31018          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31019          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31020          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31021          * @param {String} title The title bar text
31022          * @param {String} msg The message box body text
31023          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31024          * @param {Object} scope (optional) The scope of the callback function
31025          * @return {Roo.MessageBox} This message box
31026          */
31027         confirm : function(title, msg, fn, scope){
31028             this.show({
31029                 title : title,
31030                 msg : msg,
31031                 buttons: this.YESNO,
31032                 fn: fn,
31033                 scope : scope,
31034                 modal : true
31035             });
31036             return this;
31037         },
31038
31039         /**
31040          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31041          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31042          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31043          * (could also be the top-right close button) and the text that was entered will be passed as the two
31044          * parameters to the callback.
31045          * @param {String} title The title bar text
31046          * @param {String} msg The message box body text
31047          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31048          * @param {Object} scope (optional) The scope of the callback function
31049          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31050          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31051          * @return {Roo.MessageBox} This message box
31052          */
31053         prompt : function(title, msg, fn, scope, multiline){
31054             this.show({
31055                 title : title,
31056                 msg : msg,
31057                 buttons: this.OKCANCEL,
31058                 fn: fn,
31059                 minWidth:250,
31060                 scope : scope,
31061                 prompt:true,
31062                 multiline: multiline,
31063                 modal : true
31064             });
31065             return this;
31066         },
31067
31068         /**
31069          * Button config that displays a single OK button
31070          * @type Object
31071          */
31072         OK : {ok:true},
31073         /**
31074          * Button config that displays Yes and No buttons
31075          * @type Object
31076          */
31077         YESNO : {yes:true, no:true},
31078         /**
31079          * Button config that displays OK and Cancel buttons
31080          * @type Object
31081          */
31082         OKCANCEL : {ok:true, cancel:true},
31083         /**
31084          * Button config that displays Yes, No and Cancel buttons
31085          * @type Object
31086          */
31087         YESNOCANCEL : {yes:true, no:true, cancel:true},
31088
31089         /**
31090          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31091          * @type Number
31092          */
31093         defaultTextHeight : 75,
31094         /**
31095          * The maximum width in pixels of the message box (defaults to 600)
31096          * @type Number
31097          */
31098         maxWidth : 600,
31099         /**
31100          * The minimum width in pixels of the message box (defaults to 100)
31101          * @type Number
31102          */
31103         minWidth : 100,
31104         /**
31105          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31106          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31107          * @type Number
31108          */
31109         minProgressWidth : 250,
31110         /**
31111          * An object containing the default button text strings that can be overriden for localized language support.
31112          * Supported properties are: ok, cancel, yes and no.
31113          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31114          * @type Object
31115          */
31116         buttonText : {
31117             ok : "OK",
31118             cancel : "Cancel",
31119             yes : "Yes",
31120             no : "No"
31121         }
31122     };
31123 }();
31124
31125 /**
31126  * Shorthand for {@link Roo.MessageBox}
31127  */
31128 Roo.Msg = Roo.MessageBox;/*
31129  * Based on:
31130  * Ext JS Library 1.1.1
31131  * Copyright(c) 2006-2007, Ext JS, LLC.
31132  *
31133  * Originally Released Under LGPL - original licence link has changed is not relivant.
31134  *
31135  * Fork - LGPL
31136  * <script type="text/javascript">
31137  */
31138 /**
31139  * @class Roo.QuickTips
31140  * Provides attractive and customizable tooltips for any element.
31141  * @singleton
31142  */
31143 Roo.QuickTips = function(){
31144     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31145     var ce, bd, xy, dd;
31146     var visible = false, disabled = true, inited = false;
31147     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31148     
31149     var onOver = function(e){
31150         if(disabled){
31151             return;
31152         }
31153         var t = e.getTarget();
31154         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31155             return;
31156         }
31157         if(ce && t == ce.el){
31158             clearTimeout(hideProc);
31159             return;
31160         }
31161         if(t && tagEls[t.id]){
31162             tagEls[t.id].el = t;
31163             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31164             return;
31165         }
31166         var ttp, et = Roo.fly(t);
31167         var ns = cfg.namespace;
31168         if(tm.interceptTitles && t.title){
31169             ttp = t.title;
31170             t.qtip = ttp;
31171             t.removeAttribute("title");
31172             e.preventDefault();
31173         }else{
31174             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31175         }
31176         if(ttp){
31177             showProc = show.defer(tm.showDelay, tm, [{
31178                 el: t, 
31179                 text: ttp, 
31180                 width: et.getAttributeNS(ns, cfg.width),
31181                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31182                 title: et.getAttributeNS(ns, cfg.title),
31183                     cls: et.getAttributeNS(ns, cfg.cls)
31184             }]);
31185         }
31186     };
31187     
31188     var onOut = function(e){
31189         clearTimeout(showProc);
31190         var t = e.getTarget();
31191         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31192             hideProc = setTimeout(hide, tm.hideDelay);
31193         }
31194     };
31195     
31196     var onMove = function(e){
31197         if(disabled){
31198             return;
31199         }
31200         xy = e.getXY();
31201         xy[1] += 18;
31202         if(tm.trackMouse && ce){
31203             el.setXY(xy);
31204         }
31205     };
31206     
31207     var onDown = function(e){
31208         clearTimeout(showProc);
31209         clearTimeout(hideProc);
31210         if(!e.within(el)){
31211             if(tm.hideOnClick){
31212                 hide();
31213                 tm.disable();
31214                 tm.enable.defer(100, tm);
31215             }
31216         }
31217     };
31218     
31219     var getPad = function(){
31220         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31221     };
31222
31223     var show = function(o){
31224         if(disabled){
31225             return;
31226         }
31227         clearTimeout(dismissProc);
31228         ce = o;
31229         if(removeCls){ // in case manually hidden
31230             el.removeClass(removeCls);
31231             removeCls = null;
31232         }
31233         if(ce.cls){
31234             el.addClass(ce.cls);
31235             removeCls = ce.cls;
31236         }
31237         if(ce.title){
31238             tipTitle.update(ce.title);
31239             tipTitle.show();
31240         }else{
31241             tipTitle.update('');
31242             tipTitle.hide();
31243         }
31244         el.dom.style.width  = tm.maxWidth+'px';
31245         //tipBody.dom.style.width = '';
31246         tipBodyText.update(o.text);
31247         var p = getPad(), w = ce.width;
31248         if(!w){
31249             var td = tipBodyText.dom;
31250             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31251             if(aw > tm.maxWidth){
31252                 w = tm.maxWidth;
31253             }else if(aw < tm.minWidth){
31254                 w = tm.minWidth;
31255             }else{
31256                 w = aw;
31257             }
31258         }
31259         //tipBody.setWidth(w);
31260         el.setWidth(parseInt(w, 10) + p);
31261         if(ce.autoHide === false){
31262             close.setDisplayed(true);
31263             if(dd){
31264                 dd.unlock();
31265             }
31266         }else{
31267             close.setDisplayed(false);
31268             if(dd){
31269                 dd.lock();
31270             }
31271         }
31272         if(xy){
31273             el.avoidY = xy[1]-18;
31274             el.setXY(xy);
31275         }
31276         if(tm.animate){
31277             el.setOpacity(.1);
31278             el.setStyle("visibility", "visible");
31279             el.fadeIn({callback: afterShow});
31280         }else{
31281             afterShow();
31282         }
31283     };
31284     
31285     var afterShow = function(){
31286         if(ce){
31287             el.show();
31288             esc.enable();
31289             if(tm.autoDismiss && ce.autoHide !== false){
31290                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31291             }
31292         }
31293     };
31294     
31295     var hide = function(noanim){
31296         clearTimeout(dismissProc);
31297         clearTimeout(hideProc);
31298         ce = null;
31299         if(el.isVisible()){
31300             esc.disable();
31301             if(noanim !== true && tm.animate){
31302                 el.fadeOut({callback: afterHide});
31303             }else{
31304                 afterHide();
31305             } 
31306         }
31307     };
31308     
31309     var afterHide = function(){
31310         el.hide();
31311         if(removeCls){
31312             el.removeClass(removeCls);
31313             removeCls = null;
31314         }
31315     };
31316     
31317     return {
31318         /**
31319         * @cfg {Number} minWidth
31320         * The minimum width of the quick tip (defaults to 40)
31321         */
31322        minWidth : 40,
31323         /**
31324         * @cfg {Number} maxWidth
31325         * The maximum width of the quick tip (defaults to 300)
31326         */
31327        maxWidth : 300,
31328         /**
31329         * @cfg {Boolean} interceptTitles
31330         * True to automatically use the element's DOM title value if available (defaults to false)
31331         */
31332        interceptTitles : false,
31333         /**
31334         * @cfg {Boolean} trackMouse
31335         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31336         */
31337        trackMouse : false,
31338         /**
31339         * @cfg {Boolean} hideOnClick
31340         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31341         */
31342        hideOnClick : true,
31343         /**
31344         * @cfg {Number} showDelay
31345         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31346         */
31347        showDelay : 500,
31348         /**
31349         * @cfg {Number} hideDelay
31350         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31351         */
31352        hideDelay : 200,
31353         /**
31354         * @cfg {Boolean} autoHide
31355         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31356         * Used in conjunction with hideDelay.
31357         */
31358        autoHide : true,
31359         /**
31360         * @cfg {Boolean}
31361         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31362         * (defaults to true).  Used in conjunction with autoDismissDelay.
31363         */
31364        autoDismiss : true,
31365         /**
31366         * @cfg {Number}
31367         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31368         */
31369        autoDismissDelay : 5000,
31370        /**
31371         * @cfg {Boolean} animate
31372         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31373         */
31374        animate : false,
31375
31376        /**
31377         * @cfg {String} title
31378         * Title text to display (defaults to '').  This can be any valid HTML markup.
31379         */
31380         title: '',
31381        /**
31382         * @cfg {String} text
31383         * Body text to display (defaults to '').  This can be any valid HTML markup.
31384         */
31385         text : '',
31386        /**
31387         * @cfg {String} cls
31388         * A CSS class to apply to the base quick tip element (defaults to '').
31389         */
31390         cls : '',
31391        /**
31392         * @cfg {Number} width
31393         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
31394         * minWidth or maxWidth.
31395         */
31396         width : null,
31397
31398     /**
31399      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
31400      * or display QuickTips in a page.
31401      */
31402        init : function(){
31403           tm = Roo.QuickTips;
31404           cfg = tm.tagConfig;
31405           if(!inited){
31406               if(!Roo.isReady){ // allow calling of init() before onReady
31407                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
31408                   return;
31409               }
31410               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
31411               el.fxDefaults = {stopFx: true};
31412               // maximum custom styling
31413               //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>');
31414               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>');              
31415               tipTitle = el.child('h3');
31416               tipTitle.enableDisplayMode("block");
31417               tipBody = el.child('div.x-tip-bd');
31418               tipBodyText = el.child('div.x-tip-bd-inner');
31419               //bdLeft = el.child('div.x-tip-bd-left');
31420               //bdRight = el.child('div.x-tip-bd-right');
31421               close = el.child('div.x-tip-close');
31422               close.enableDisplayMode("block");
31423               close.on("click", hide);
31424               var d = Roo.get(document);
31425               d.on("mousedown", onDown);
31426               d.on("mouseover", onOver);
31427               d.on("mouseout", onOut);
31428               d.on("mousemove", onMove);
31429               esc = d.addKeyListener(27, hide);
31430               esc.disable();
31431               if(Roo.dd.DD){
31432                   dd = el.initDD("default", null, {
31433                       onDrag : function(){
31434                           el.sync();  
31435                       }
31436                   });
31437                   dd.setHandleElId(tipTitle.id);
31438                   dd.lock();
31439               }
31440               inited = true;
31441           }
31442           this.enable(); 
31443        },
31444
31445     /**
31446      * Configures a new quick tip instance and assigns it to a target element.  The following config options
31447      * are supported:
31448      * <pre>
31449 Property    Type                   Description
31450 ----------  ---------------------  ------------------------------------------------------------------------
31451 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
31452      * </ul>
31453      * @param {Object} config The config object
31454      */
31455        register : function(config){
31456            var cs = config instanceof Array ? config : arguments;
31457            for(var i = 0, len = cs.length; i < len; i++) {
31458                var c = cs[i];
31459                var target = c.target;
31460                if(target){
31461                    if(target instanceof Array){
31462                        for(var j = 0, jlen = target.length; j < jlen; j++){
31463                            tagEls[target[j]] = c;
31464                        }
31465                    }else{
31466                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
31467                    }
31468                }
31469            }
31470        },
31471
31472     /**
31473      * Removes this quick tip from its element and destroys it.
31474      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
31475      */
31476        unregister : function(el){
31477            delete tagEls[Roo.id(el)];
31478        },
31479
31480     /**
31481      * Enable this quick tip.
31482      */
31483        enable : function(){
31484            if(inited && disabled){
31485                locks.pop();
31486                if(locks.length < 1){
31487                    disabled = false;
31488                }
31489            }
31490        },
31491
31492     /**
31493      * Disable this quick tip.
31494      */
31495        disable : function(){
31496           disabled = true;
31497           clearTimeout(showProc);
31498           clearTimeout(hideProc);
31499           clearTimeout(dismissProc);
31500           if(ce){
31501               hide(true);
31502           }
31503           locks.push(1);
31504        },
31505
31506     /**
31507      * Returns true if the quick tip is enabled, else false.
31508      */
31509        isEnabled : function(){
31510             return !disabled;
31511        },
31512
31513         // private
31514        tagConfig : {
31515            namespace : "ext",
31516            attribute : "qtip",
31517            width : "width",
31518            target : "target",
31519            title : "qtitle",
31520            hide : "hide",
31521            cls : "qclass"
31522        }
31523    };
31524 }();
31525
31526 // backwards compat
31527 Roo.QuickTips.tips = Roo.QuickTips.register;/*
31528  * Based on:
31529  * Ext JS Library 1.1.1
31530  * Copyright(c) 2006-2007, Ext JS, LLC.
31531  *
31532  * Originally Released Under LGPL - original licence link has changed is not relivant.
31533  *
31534  * Fork - LGPL
31535  * <script type="text/javascript">
31536  */
31537  
31538
31539 /**
31540  * @class Roo.tree.TreePanel
31541  * @extends Roo.data.Tree
31542
31543  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
31544  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
31545  * @cfg {Boolean} enableDD true to enable drag and drop
31546  * @cfg {Boolean} enableDrag true to enable just drag
31547  * @cfg {Boolean} enableDrop true to enable just drop
31548  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
31549  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
31550  * @cfg {String} ddGroup The DD group this TreePanel belongs to
31551  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
31552  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
31553  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
31554  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
31555  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
31556  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
31557  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
31558  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
31559  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
31560  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
31561  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
31562  * @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>
31563  * @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>
31564  * 
31565  * @constructor
31566  * @param {String/HTMLElement/Element} el The container element
31567  * @param {Object} config
31568  */
31569 Roo.tree.TreePanel = function(el, config){
31570     var root = false;
31571     var loader = false;
31572     if (config.root) {
31573         root = config.root;
31574         delete config.root;
31575     }
31576     if (config.loader) {
31577         loader = config.loader;
31578         delete config.loader;
31579     }
31580     
31581     Roo.apply(this, config);
31582     Roo.tree.TreePanel.superclass.constructor.call(this);
31583     this.el = Roo.get(el);
31584     this.el.addClass('x-tree');
31585     //console.log(root);
31586     if (root) {
31587         this.setRootNode( Roo.factory(root, Roo.tree));
31588     }
31589     if (loader) {
31590         this.loader = Roo.factory(loader, Roo.tree);
31591     }
31592    /**
31593     * Read-only. The id of the container element becomes this TreePanel's id.
31594     */
31595     this.id = this.el.id;
31596     this.addEvents({
31597         /**
31598         * @event beforeload
31599         * Fires before a node is loaded, return false to cancel
31600         * @param {Node} node The node being loaded
31601         */
31602         "beforeload" : true,
31603         /**
31604         * @event load
31605         * Fires when a node is loaded
31606         * @param {Node} node The node that was loaded
31607         */
31608         "load" : true,
31609         /**
31610         * @event textchange
31611         * Fires when the text for a node is changed
31612         * @param {Node} node The node
31613         * @param {String} text The new text
31614         * @param {String} oldText The old text
31615         */
31616         "textchange" : true,
31617         /**
31618         * @event beforeexpand
31619         * Fires before a node is expanded, return false to cancel.
31620         * @param {Node} node The node
31621         * @param {Boolean} deep
31622         * @param {Boolean} anim
31623         */
31624         "beforeexpand" : true,
31625         /**
31626         * @event beforecollapse
31627         * Fires before a node is collapsed, return false to cancel.
31628         * @param {Node} node The node
31629         * @param {Boolean} deep
31630         * @param {Boolean} anim
31631         */
31632         "beforecollapse" : true,
31633         /**
31634         * @event expand
31635         * Fires when a node is expanded
31636         * @param {Node} node The node
31637         */
31638         "expand" : true,
31639         /**
31640         * @event disabledchange
31641         * Fires when the disabled status of a node changes
31642         * @param {Node} node The node
31643         * @param {Boolean} disabled
31644         */
31645         "disabledchange" : true,
31646         /**
31647         * @event collapse
31648         * Fires when a node is collapsed
31649         * @param {Node} node The node
31650         */
31651         "collapse" : true,
31652         /**
31653         * @event beforeclick
31654         * Fires before click processing on a node. Return false to cancel the default action.
31655         * @param {Node} node The node
31656         * @param {Roo.EventObject} e The event object
31657         */
31658         "beforeclick":true,
31659         /**
31660         * @event checkchange
31661         * Fires when a node with a checkbox's checked property changes
31662         * @param {Node} this This node
31663         * @param {Boolean} checked
31664         */
31665         "checkchange":true,
31666         /**
31667         * @event click
31668         * Fires when a node is clicked
31669         * @param {Node} node The node
31670         * @param {Roo.EventObject} e The event object
31671         */
31672         "click":true,
31673         /**
31674         * @event dblclick
31675         * Fires when a node is double clicked
31676         * @param {Node} node The node
31677         * @param {Roo.EventObject} e The event object
31678         */
31679         "dblclick":true,
31680         /**
31681         * @event contextmenu
31682         * Fires when a node is right clicked
31683         * @param {Node} node The node
31684         * @param {Roo.EventObject} e The event object
31685         */
31686         "contextmenu":true,
31687         /**
31688         * @event beforechildrenrendered
31689         * Fires right before the child nodes for a node are rendered
31690         * @param {Node} node The node
31691         */
31692         "beforechildrenrendered":true,
31693         /**
31694         * @event startdrag
31695         * Fires when a node starts being dragged
31696         * @param {Roo.tree.TreePanel} this
31697         * @param {Roo.tree.TreeNode} node
31698         * @param {event} e The raw browser event
31699         */ 
31700        "startdrag" : true,
31701        /**
31702         * @event enddrag
31703         * Fires when a drag operation is complete
31704         * @param {Roo.tree.TreePanel} this
31705         * @param {Roo.tree.TreeNode} node
31706         * @param {event} e The raw browser event
31707         */
31708        "enddrag" : true,
31709        /**
31710         * @event dragdrop
31711         * Fires when a dragged node is dropped on a valid DD target
31712         * @param {Roo.tree.TreePanel} this
31713         * @param {Roo.tree.TreeNode} node
31714         * @param {DD} dd The dd it was dropped on
31715         * @param {event} e The raw browser event
31716         */
31717        "dragdrop" : true,
31718        /**
31719         * @event beforenodedrop
31720         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31721         * passed to handlers has the following properties:<br />
31722         * <ul style="padding:5px;padding-left:16px;">
31723         * <li>tree - The TreePanel</li>
31724         * <li>target - The node being targeted for the drop</li>
31725         * <li>data - The drag data from the drag source</li>
31726         * <li>point - The point of the drop - append, above or below</li>
31727         * <li>source - The drag source</li>
31728         * <li>rawEvent - Raw mouse event</li>
31729         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31730         * to be inserted by setting them on this object.</li>
31731         * <li>cancel - Set this to true to cancel the drop.</li>
31732         * </ul>
31733         * @param {Object} dropEvent
31734         */
31735        "beforenodedrop" : true,
31736        /**
31737         * @event nodedrop
31738         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31739         * passed to handlers has the following properties:<br />
31740         * <ul style="padding:5px;padding-left:16px;">
31741         * <li>tree - The TreePanel</li>
31742         * <li>target - The node being targeted for the drop</li>
31743         * <li>data - The drag data from the drag source</li>
31744         * <li>point - The point of the drop - append, above or below</li>
31745         * <li>source - The drag source</li>
31746         * <li>rawEvent - Raw mouse event</li>
31747         * <li>dropNode - Dropped node(s).</li>
31748         * </ul>
31749         * @param {Object} dropEvent
31750         */
31751        "nodedrop" : true,
31752         /**
31753         * @event nodedragover
31754         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31755         * passed to handlers has the following properties:<br />
31756         * <ul style="padding:5px;padding-left:16px;">
31757         * <li>tree - The TreePanel</li>
31758         * <li>target - The node being targeted for the drop</li>
31759         * <li>data - The drag data from the drag source</li>
31760         * <li>point - The point of the drop - append, above or below</li>
31761         * <li>source - The drag source</li>
31762         * <li>rawEvent - Raw mouse event</li>
31763         * <li>dropNode - Drop node(s) provided by the source.</li>
31764         * <li>cancel - Set this to true to signal drop not allowed.</li>
31765         * </ul>
31766         * @param {Object} dragOverEvent
31767         */
31768        "nodedragover" : true
31769         
31770     });
31771     if(this.singleExpand){
31772        this.on("beforeexpand", this.restrictExpand, this);
31773     }
31774     if (this.editor) {
31775         this.editor.tree = this;
31776         this.editor = Roo.factory(this.editor, Roo.tree);
31777     }
31778     
31779     if (this.selModel) {
31780         this.selModel = Roo.factory(this.selModel, Roo.tree);
31781     }
31782    
31783 };
31784 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31785     rootVisible : true,
31786     animate: Roo.enableFx,
31787     lines : true,
31788     enableDD : false,
31789     hlDrop : Roo.enableFx,
31790   
31791     renderer: false,
31792     
31793     rendererTip: false,
31794     // private
31795     restrictExpand : function(node){
31796         var p = node.parentNode;
31797         if(p){
31798             if(p.expandedChild && p.expandedChild.parentNode == p){
31799                 p.expandedChild.collapse();
31800             }
31801             p.expandedChild = node;
31802         }
31803     },
31804
31805     // private override
31806     setRootNode : function(node){
31807         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31808         if(!this.rootVisible){
31809             node.ui = new Roo.tree.RootTreeNodeUI(node);
31810         }
31811         return node;
31812     },
31813
31814     /**
31815      * Returns the container element for this TreePanel
31816      */
31817     getEl : function(){
31818         return this.el;
31819     },
31820
31821     /**
31822      * Returns the default TreeLoader for this TreePanel
31823      */
31824     getLoader : function(){
31825         return this.loader;
31826     },
31827
31828     /**
31829      * Expand all nodes
31830      */
31831     expandAll : function(){
31832         this.root.expand(true);
31833     },
31834
31835     /**
31836      * Collapse all nodes
31837      */
31838     collapseAll : function(){
31839         this.root.collapse(true);
31840     },
31841
31842     /**
31843      * Returns the selection model used by this TreePanel
31844      */
31845     getSelectionModel : function(){
31846         if(!this.selModel){
31847             this.selModel = new Roo.tree.DefaultSelectionModel();
31848         }
31849         return this.selModel;
31850     },
31851
31852     /**
31853      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31854      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31855      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31856      * @return {Array}
31857      */
31858     getChecked : function(a, startNode){
31859         startNode = startNode || this.root;
31860         var r = [];
31861         var f = function(){
31862             if(this.attributes.checked){
31863                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31864             }
31865         }
31866         startNode.cascade(f);
31867         return r;
31868     },
31869
31870     /**
31871      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31872      * @param {String} path
31873      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31874      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31875      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31876      */
31877     expandPath : function(path, attr, callback){
31878         attr = attr || "id";
31879         var keys = path.split(this.pathSeparator);
31880         var curNode = this.root;
31881         if(curNode.attributes[attr] != keys[1]){ // invalid root
31882             if(callback){
31883                 callback(false, null);
31884             }
31885             return;
31886         }
31887         var index = 1;
31888         var f = function(){
31889             if(++index == keys.length){
31890                 if(callback){
31891                     callback(true, curNode);
31892                 }
31893                 return;
31894             }
31895             var c = curNode.findChild(attr, keys[index]);
31896             if(!c){
31897                 if(callback){
31898                     callback(false, curNode);
31899                 }
31900                 return;
31901             }
31902             curNode = c;
31903             c.expand(false, false, f);
31904         };
31905         curNode.expand(false, false, f);
31906     },
31907
31908     /**
31909      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31910      * @param {String} path
31911      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31912      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31913      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31914      */
31915     selectPath : function(path, attr, callback){
31916         attr = attr || "id";
31917         var keys = path.split(this.pathSeparator);
31918         var v = keys.pop();
31919         if(keys.length > 0){
31920             var f = function(success, node){
31921                 if(success && node){
31922                     var n = node.findChild(attr, v);
31923                     if(n){
31924                         n.select();
31925                         if(callback){
31926                             callback(true, n);
31927                         }
31928                     }else if(callback){
31929                         callback(false, n);
31930                     }
31931                 }else{
31932                     if(callback){
31933                         callback(false, n);
31934                     }
31935                 }
31936             };
31937             this.expandPath(keys.join(this.pathSeparator), attr, f);
31938         }else{
31939             this.root.select();
31940             if(callback){
31941                 callback(true, this.root);
31942             }
31943         }
31944     },
31945
31946     getTreeEl : function(){
31947         return this.el;
31948     },
31949
31950     /**
31951      * Trigger rendering of this TreePanel
31952      */
31953     render : function(){
31954         if (this.innerCt) {
31955             return this; // stop it rendering more than once!!
31956         }
31957         
31958         this.innerCt = this.el.createChild({tag:"ul",
31959                cls:"x-tree-root-ct " +
31960                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31961
31962         if(this.containerScroll){
31963             Roo.dd.ScrollManager.register(this.el);
31964         }
31965         if((this.enableDD || this.enableDrop) && !this.dropZone){
31966            /**
31967             * The dropZone used by this tree if drop is enabled
31968             * @type Roo.tree.TreeDropZone
31969             */
31970              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31971                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31972            });
31973         }
31974         if((this.enableDD || this.enableDrag) && !this.dragZone){
31975            /**
31976             * The dragZone used by this tree if drag is enabled
31977             * @type Roo.tree.TreeDragZone
31978             */
31979             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31980                ddGroup: this.ddGroup || "TreeDD",
31981                scroll: this.ddScroll
31982            });
31983         }
31984         this.getSelectionModel().init(this);
31985         if (!this.root) {
31986             Roo.log("ROOT not set in tree");
31987             return this;
31988         }
31989         this.root.render();
31990         if(!this.rootVisible){
31991             this.root.renderChildren();
31992         }
31993         return this;
31994     }
31995 });/*
31996  * Based on:
31997  * Ext JS Library 1.1.1
31998  * Copyright(c) 2006-2007, Ext JS, LLC.
31999  *
32000  * Originally Released Under LGPL - original licence link has changed is not relivant.
32001  *
32002  * Fork - LGPL
32003  * <script type="text/javascript">
32004  */
32005  
32006
32007 /**
32008  * @class Roo.tree.DefaultSelectionModel
32009  * @extends Roo.util.Observable
32010  * The default single selection for a TreePanel.
32011  * @param {Object} cfg Configuration
32012  */
32013 Roo.tree.DefaultSelectionModel = function(cfg){
32014    this.selNode = null;
32015    
32016    
32017    
32018    this.addEvents({
32019        /**
32020         * @event selectionchange
32021         * Fires when the selected node changes
32022         * @param {DefaultSelectionModel} this
32023         * @param {TreeNode} node the new selection
32024         */
32025        "selectionchange" : true,
32026
32027        /**
32028         * @event beforeselect
32029         * Fires before the selected node changes, return false to cancel the change
32030         * @param {DefaultSelectionModel} this
32031         * @param {TreeNode} node the new selection
32032         * @param {TreeNode} node the old selection
32033         */
32034        "beforeselect" : true
32035    });
32036    
32037     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32038 };
32039
32040 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32041     init : function(tree){
32042         this.tree = tree;
32043         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32044         tree.on("click", this.onNodeClick, this);
32045     },
32046     
32047     onNodeClick : function(node, e){
32048         if (e.ctrlKey && this.selNode == node)  {
32049             this.unselect(node);
32050             return;
32051         }
32052         this.select(node);
32053     },
32054     
32055     /**
32056      * Select a node.
32057      * @param {TreeNode} node The node to select
32058      * @return {TreeNode} The selected node
32059      */
32060     select : function(node){
32061         var last = this.selNode;
32062         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32063             if(last){
32064                 last.ui.onSelectedChange(false);
32065             }
32066             this.selNode = node;
32067             node.ui.onSelectedChange(true);
32068             this.fireEvent("selectionchange", this, node, last);
32069         }
32070         return node;
32071     },
32072     
32073     /**
32074      * Deselect a node.
32075      * @param {TreeNode} node The node to unselect
32076      */
32077     unselect : function(node){
32078         if(this.selNode == node){
32079             this.clearSelections();
32080         }    
32081     },
32082     
32083     /**
32084      * Clear all selections
32085      */
32086     clearSelections : function(){
32087         var n = this.selNode;
32088         if(n){
32089             n.ui.onSelectedChange(false);
32090             this.selNode = null;
32091             this.fireEvent("selectionchange", this, null);
32092         }
32093         return n;
32094     },
32095     
32096     /**
32097      * Get the selected node
32098      * @return {TreeNode} The selected node
32099      */
32100     getSelectedNode : function(){
32101         return this.selNode;    
32102     },
32103     
32104     /**
32105      * Returns true if the node is selected
32106      * @param {TreeNode} node The node to check
32107      * @return {Boolean}
32108      */
32109     isSelected : function(node){
32110         return this.selNode == node;  
32111     },
32112
32113     /**
32114      * Selects the node above the selected node in the tree, intelligently walking the nodes
32115      * @return TreeNode The new selection
32116      */
32117     selectPrevious : function(){
32118         var s = this.selNode || this.lastSelNode;
32119         if(!s){
32120             return null;
32121         }
32122         var ps = s.previousSibling;
32123         if(ps){
32124             if(!ps.isExpanded() || ps.childNodes.length < 1){
32125                 return this.select(ps);
32126             } else{
32127                 var lc = ps.lastChild;
32128                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32129                     lc = lc.lastChild;
32130                 }
32131                 return this.select(lc);
32132             }
32133         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32134             return this.select(s.parentNode);
32135         }
32136         return null;
32137     },
32138
32139     /**
32140      * Selects the node above the selected node in the tree, intelligently walking the nodes
32141      * @return TreeNode The new selection
32142      */
32143     selectNext : function(){
32144         var s = this.selNode || this.lastSelNode;
32145         if(!s){
32146             return null;
32147         }
32148         if(s.firstChild && s.isExpanded()){
32149              return this.select(s.firstChild);
32150          }else if(s.nextSibling){
32151              return this.select(s.nextSibling);
32152          }else if(s.parentNode){
32153             var newS = null;
32154             s.parentNode.bubble(function(){
32155                 if(this.nextSibling){
32156                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32157                     return false;
32158                 }
32159             });
32160             return newS;
32161          }
32162         return null;
32163     },
32164
32165     onKeyDown : function(e){
32166         var s = this.selNode || this.lastSelNode;
32167         // undesirable, but required
32168         var sm = this;
32169         if(!s){
32170             return;
32171         }
32172         var k = e.getKey();
32173         switch(k){
32174              case e.DOWN:
32175                  e.stopEvent();
32176                  this.selectNext();
32177              break;
32178              case e.UP:
32179                  e.stopEvent();
32180                  this.selectPrevious();
32181              break;
32182              case e.RIGHT:
32183                  e.preventDefault();
32184                  if(s.hasChildNodes()){
32185                      if(!s.isExpanded()){
32186                          s.expand();
32187                      }else if(s.firstChild){
32188                          this.select(s.firstChild, e);
32189                      }
32190                  }
32191              break;
32192              case e.LEFT:
32193                  e.preventDefault();
32194                  if(s.hasChildNodes() && s.isExpanded()){
32195                      s.collapse();
32196                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32197                      this.select(s.parentNode, e);
32198                  }
32199              break;
32200         };
32201     }
32202 });
32203
32204 /**
32205  * @class Roo.tree.MultiSelectionModel
32206  * @extends Roo.util.Observable
32207  * Multi selection for a TreePanel.
32208  * @param {Object} cfg Configuration
32209  */
32210 Roo.tree.MultiSelectionModel = function(){
32211    this.selNodes = [];
32212    this.selMap = {};
32213    this.addEvents({
32214        /**
32215         * @event selectionchange
32216         * Fires when the selected nodes change
32217         * @param {MultiSelectionModel} this
32218         * @param {Array} nodes Array of the selected nodes
32219         */
32220        "selectionchange" : true
32221    });
32222    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32223    
32224 };
32225
32226 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32227     init : function(tree){
32228         this.tree = tree;
32229         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32230         tree.on("click", this.onNodeClick, this);
32231     },
32232     
32233     onNodeClick : function(node, e){
32234         this.select(node, e, e.ctrlKey);
32235     },
32236     
32237     /**
32238      * Select a node.
32239      * @param {TreeNode} node The node to select
32240      * @param {EventObject} e (optional) An event associated with the selection
32241      * @param {Boolean} keepExisting True to retain existing selections
32242      * @return {TreeNode} The selected node
32243      */
32244     select : function(node, e, keepExisting){
32245         if(keepExisting !== true){
32246             this.clearSelections(true);
32247         }
32248         if(this.isSelected(node)){
32249             this.lastSelNode = node;
32250             return node;
32251         }
32252         this.selNodes.push(node);
32253         this.selMap[node.id] = node;
32254         this.lastSelNode = node;
32255         node.ui.onSelectedChange(true);
32256         this.fireEvent("selectionchange", this, this.selNodes);
32257         return node;
32258     },
32259     
32260     /**
32261      * Deselect a node.
32262      * @param {TreeNode} node The node to unselect
32263      */
32264     unselect : function(node){
32265         if(this.selMap[node.id]){
32266             node.ui.onSelectedChange(false);
32267             var sn = this.selNodes;
32268             var index = -1;
32269             if(sn.indexOf){
32270                 index = sn.indexOf(node);
32271             }else{
32272                 for(var i = 0, len = sn.length; i < len; i++){
32273                     if(sn[i] == node){
32274                         index = i;
32275                         break;
32276                     }
32277                 }
32278             }
32279             if(index != -1){
32280                 this.selNodes.splice(index, 1);
32281             }
32282             delete this.selMap[node.id];
32283             this.fireEvent("selectionchange", this, this.selNodes);
32284         }
32285     },
32286     
32287     /**
32288      * Clear all selections
32289      */
32290     clearSelections : function(suppressEvent){
32291         var sn = this.selNodes;
32292         if(sn.length > 0){
32293             for(var i = 0, len = sn.length; i < len; i++){
32294                 sn[i].ui.onSelectedChange(false);
32295             }
32296             this.selNodes = [];
32297             this.selMap = {};
32298             if(suppressEvent !== true){
32299                 this.fireEvent("selectionchange", this, this.selNodes);
32300             }
32301         }
32302     },
32303     
32304     /**
32305      * Returns true if the node is selected
32306      * @param {TreeNode} node The node to check
32307      * @return {Boolean}
32308      */
32309     isSelected : function(node){
32310         return this.selMap[node.id] ? true : false;  
32311     },
32312     
32313     /**
32314      * Returns an array of the selected nodes
32315      * @return {Array}
32316      */
32317     getSelectedNodes : function(){
32318         return this.selNodes;    
32319     },
32320
32321     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32322
32323     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32324
32325     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32326 });/*
32327  * Based on:
32328  * Ext JS Library 1.1.1
32329  * Copyright(c) 2006-2007, Ext JS, LLC.
32330  *
32331  * Originally Released Under LGPL - original licence link has changed is not relivant.
32332  *
32333  * Fork - LGPL
32334  * <script type="text/javascript">
32335  */
32336  
32337 /**
32338  * @class Roo.tree.TreeNode
32339  * @extends Roo.data.Node
32340  * @cfg {String} text The text for this node
32341  * @cfg {Boolean} expanded true to start the node expanded
32342  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32343  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32344  * @cfg {Boolean} disabled true to start the node disabled
32345  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32346  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32347  * @cfg {String} cls A css class to be added to the node
32348  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32349  * @cfg {String} href URL of the link used for the node (defaults to #)
32350  * @cfg {String} hrefTarget target frame for the link
32351  * @cfg {String} qtip An Ext QuickTip for the node
32352  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32353  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32354  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32355  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32356  * (defaults to undefined with no checkbox rendered)
32357  * @constructor
32358  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32359  */
32360 Roo.tree.TreeNode = function(attributes){
32361     attributes = attributes || {};
32362     if(typeof attributes == "string"){
32363         attributes = {text: attributes};
32364     }
32365     this.childrenRendered = false;
32366     this.rendered = false;
32367     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32368     this.expanded = attributes.expanded === true;
32369     this.isTarget = attributes.isTarget !== false;
32370     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32371     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32372
32373     /**
32374      * Read-only. The text for this node. To change it use setText().
32375      * @type String
32376      */
32377     this.text = attributes.text;
32378     /**
32379      * True if this node is disabled.
32380      * @type Boolean
32381      */
32382     this.disabled = attributes.disabled === true;
32383
32384     this.addEvents({
32385         /**
32386         * @event textchange
32387         * Fires when the text for this node is changed
32388         * @param {Node} this This node
32389         * @param {String} text The new text
32390         * @param {String} oldText The old text
32391         */
32392         "textchange" : true,
32393         /**
32394         * @event beforeexpand
32395         * Fires before this node is expanded, return false to cancel.
32396         * @param {Node} this This node
32397         * @param {Boolean} deep
32398         * @param {Boolean} anim
32399         */
32400         "beforeexpand" : true,
32401         /**
32402         * @event beforecollapse
32403         * Fires before this node is collapsed, return false to cancel.
32404         * @param {Node} this This node
32405         * @param {Boolean} deep
32406         * @param {Boolean} anim
32407         */
32408         "beforecollapse" : true,
32409         /**
32410         * @event expand
32411         * Fires when this node is expanded
32412         * @param {Node} this This node
32413         */
32414         "expand" : true,
32415         /**
32416         * @event disabledchange
32417         * Fires when the disabled status of this node changes
32418         * @param {Node} this This node
32419         * @param {Boolean} disabled
32420         */
32421         "disabledchange" : true,
32422         /**
32423         * @event collapse
32424         * Fires when this node is collapsed
32425         * @param {Node} this This node
32426         */
32427         "collapse" : true,
32428         /**
32429         * @event beforeclick
32430         * Fires before click processing. Return false to cancel the default action.
32431         * @param {Node} this This node
32432         * @param {Roo.EventObject} e The event object
32433         */
32434         "beforeclick":true,
32435         /**
32436         * @event checkchange
32437         * Fires when a node with a checkbox's checked property changes
32438         * @param {Node} this This node
32439         * @param {Boolean} checked
32440         */
32441         "checkchange":true,
32442         /**
32443         * @event click
32444         * Fires when this node is clicked
32445         * @param {Node} this This node
32446         * @param {Roo.EventObject} e The event object
32447         */
32448         "click":true,
32449         /**
32450         * @event dblclick
32451         * Fires when this node is double clicked
32452         * @param {Node} this This node
32453         * @param {Roo.EventObject} e The event object
32454         */
32455         "dblclick":true,
32456         /**
32457         * @event contextmenu
32458         * Fires when this node is right clicked
32459         * @param {Node} this This node
32460         * @param {Roo.EventObject} e The event object
32461         */
32462         "contextmenu":true,
32463         /**
32464         * @event beforechildrenrendered
32465         * Fires right before the child nodes for this node are rendered
32466         * @param {Node} this This node
32467         */
32468         "beforechildrenrendered":true
32469     });
32470
32471     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
32472
32473     /**
32474      * Read-only. The UI for this node
32475      * @type TreeNodeUI
32476      */
32477     this.ui = new uiClass(this);
32478     
32479     // finally support items[]
32480     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
32481         return;
32482     }
32483     
32484     
32485     Roo.each(this.attributes.items, function(c) {
32486         this.appendChild(Roo.factory(c,Roo.Tree));
32487     }, this);
32488     delete this.attributes.items;
32489     
32490     
32491     
32492 };
32493 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
32494     preventHScroll: true,
32495     /**
32496      * Returns true if this node is expanded
32497      * @return {Boolean}
32498      */
32499     isExpanded : function(){
32500         return this.expanded;
32501     },
32502
32503     /**
32504      * Returns the UI object for this node
32505      * @return {TreeNodeUI}
32506      */
32507     getUI : function(){
32508         return this.ui;
32509     },
32510
32511     // private override
32512     setFirstChild : function(node){
32513         var of = this.firstChild;
32514         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
32515         if(this.childrenRendered && of && node != of){
32516             of.renderIndent(true, true);
32517         }
32518         if(this.rendered){
32519             this.renderIndent(true, true);
32520         }
32521     },
32522
32523     // private override
32524     setLastChild : function(node){
32525         var ol = this.lastChild;
32526         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
32527         if(this.childrenRendered && ol && node != ol){
32528             ol.renderIndent(true, true);
32529         }
32530         if(this.rendered){
32531             this.renderIndent(true, true);
32532         }
32533     },
32534
32535     // these methods are overridden to provide lazy rendering support
32536     // private override
32537     appendChild : function()
32538     {
32539         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
32540         if(node && this.childrenRendered){
32541             node.render();
32542         }
32543         this.ui.updateExpandIcon();
32544         return node;
32545     },
32546
32547     // private override
32548     removeChild : function(node){
32549         this.ownerTree.getSelectionModel().unselect(node);
32550         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
32551         // if it's been rendered remove dom node
32552         if(this.childrenRendered){
32553             node.ui.remove();
32554         }
32555         if(this.childNodes.length < 1){
32556             this.collapse(false, false);
32557         }else{
32558             this.ui.updateExpandIcon();
32559         }
32560         if(!this.firstChild) {
32561             this.childrenRendered = false;
32562         }
32563         return node;
32564     },
32565
32566     // private override
32567     insertBefore : function(node, refNode){
32568         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
32569         if(newNode && refNode && this.childrenRendered){
32570             node.render();
32571         }
32572         this.ui.updateExpandIcon();
32573         return newNode;
32574     },
32575
32576     /**
32577      * Sets the text for this node
32578      * @param {String} text
32579      */
32580     setText : function(text){
32581         var oldText = this.text;
32582         this.text = text;
32583         this.attributes.text = text;
32584         if(this.rendered){ // event without subscribing
32585             this.ui.onTextChange(this, text, oldText);
32586         }
32587         this.fireEvent("textchange", this, text, oldText);
32588     },
32589
32590     /**
32591      * Triggers selection of this node
32592      */
32593     select : function(){
32594         this.getOwnerTree().getSelectionModel().select(this);
32595     },
32596
32597     /**
32598      * Triggers deselection of this node
32599      */
32600     unselect : function(){
32601         this.getOwnerTree().getSelectionModel().unselect(this);
32602     },
32603
32604     /**
32605      * Returns true if this node is selected
32606      * @return {Boolean}
32607      */
32608     isSelected : function(){
32609         return this.getOwnerTree().getSelectionModel().isSelected(this);
32610     },
32611
32612     /**
32613      * Expand this node.
32614      * @param {Boolean} deep (optional) True to expand all children as well
32615      * @param {Boolean} anim (optional) false to cancel the default animation
32616      * @param {Function} callback (optional) A callback to be called when
32617      * expanding this node completes (does not wait for deep expand to complete).
32618      * Called with 1 parameter, this node.
32619      */
32620     expand : function(deep, anim, callback){
32621         if(!this.expanded){
32622             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
32623                 return;
32624             }
32625             if(!this.childrenRendered){
32626                 this.renderChildren();
32627             }
32628             this.expanded = true;
32629             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
32630                 this.ui.animExpand(function(){
32631                     this.fireEvent("expand", this);
32632                     if(typeof callback == "function"){
32633                         callback(this);
32634                     }
32635                     if(deep === true){
32636                         this.expandChildNodes(true);
32637                     }
32638                 }.createDelegate(this));
32639                 return;
32640             }else{
32641                 this.ui.expand();
32642                 this.fireEvent("expand", this);
32643                 if(typeof callback == "function"){
32644                     callback(this);
32645                 }
32646             }
32647         }else{
32648            if(typeof callback == "function"){
32649                callback(this);
32650            }
32651         }
32652         if(deep === true){
32653             this.expandChildNodes(true);
32654         }
32655     },
32656
32657     isHiddenRoot : function(){
32658         return this.isRoot && !this.getOwnerTree().rootVisible;
32659     },
32660
32661     /**
32662      * Collapse this node.
32663      * @param {Boolean} deep (optional) True to collapse all children as well
32664      * @param {Boolean} anim (optional) false to cancel the default animation
32665      */
32666     collapse : function(deep, anim){
32667         if(this.expanded && !this.isHiddenRoot()){
32668             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32669                 return;
32670             }
32671             this.expanded = false;
32672             if((this.getOwnerTree().animate && anim !== false) || anim){
32673                 this.ui.animCollapse(function(){
32674                     this.fireEvent("collapse", this);
32675                     if(deep === true){
32676                         this.collapseChildNodes(true);
32677                     }
32678                 }.createDelegate(this));
32679                 return;
32680             }else{
32681                 this.ui.collapse();
32682                 this.fireEvent("collapse", this);
32683             }
32684         }
32685         if(deep === true){
32686             var cs = this.childNodes;
32687             for(var i = 0, len = cs.length; i < len; i++) {
32688                 cs[i].collapse(true, false);
32689             }
32690         }
32691     },
32692
32693     // private
32694     delayedExpand : function(delay){
32695         if(!this.expandProcId){
32696             this.expandProcId = this.expand.defer(delay, this);
32697         }
32698     },
32699
32700     // private
32701     cancelExpand : function(){
32702         if(this.expandProcId){
32703             clearTimeout(this.expandProcId);
32704         }
32705         this.expandProcId = false;
32706     },
32707
32708     /**
32709      * Toggles expanded/collapsed state of the node
32710      */
32711     toggle : function(){
32712         if(this.expanded){
32713             this.collapse();
32714         }else{
32715             this.expand();
32716         }
32717     },
32718
32719     /**
32720      * Ensures all parent nodes are expanded
32721      */
32722     ensureVisible : function(callback){
32723         var tree = this.getOwnerTree();
32724         tree.expandPath(this.parentNode.getPath(), false, function(){
32725             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32726             Roo.callback(callback);
32727         }.createDelegate(this));
32728     },
32729
32730     /**
32731      * Expand all child nodes
32732      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32733      */
32734     expandChildNodes : function(deep){
32735         var cs = this.childNodes;
32736         for(var i = 0, len = cs.length; i < len; i++) {
32737                 cs[i].expand(deep);
32738         }
32739     },
32740
32741     /**
32742      * Collapse all child nodes
32743      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32744      */
32745     collapseChildNodes : function(deep){
32746         var cs = this.childNodes;
32747         for(var i = 0, len = cs.length; i < len; i++) {
32748                 cs[i].collapse(deep);
32749         }
32750     },
32751
32752     /**
32753      * Disables this node
32754      */
32755     disable : function(){
32756         this.disabled = true;
32757         this.unselect();
32758         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32759             this.ui.onDisableChange(this, true);
32760         }
32761         this.fireEvent("disabledchange", this, true);
32762     },
32763
32764     /**
32765      * Enables this node
32766      */
32767     enable : function(){
32768         this.disabled = false;
32769         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32770             this.ui.onDisableChange(this, false);
32771         }
32772         this.fireEvent("disabledchange", this, false);
32773     },
32774
32775     // private
32776     renderChildren : function(suppressEvent){
32777         if(suppressEvent !== false){
32778             this.fireEvent("beforechildrenrendered", this);
32779         }
32780         var cs = this.childNodes;
32781         for(var i = 0, len = cs.length; i < len; i++){
32782             cs[i].render(true);
32783         }
32784         this.childrenRendered = true;
32785     },
32786
32787     // private
32788     sort : function(fn, scope){
32789         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32790         if(this.childrenRendered){
32791             var cs = this.childNodes;
32792             for(var i = 0, len = cs.length; i < len; i++){
32793                 cs[i].render(true);
32794             }
32795         }
32796     },
32797
32798     // private
32799     render : function(bulkRender){
32800         this.ui.render(bulkRender);
32801         if(!this.rendered){
32802             this.rendered = true;
32803             if(this.expanded){
32804                 this.expanded = false;
32805                 this.expand(false, false);
32806             }
32807         }
32808     },
32809
32810     // private
32811     renderIndent : function(deep, refresh){
32812         if(refresh){
32813             this.ui.childIndent = null;
32814         }
32815         this.ui.renderIndent();
32816         if(deep === true && this.childrenRendered){
32817             var cs = this.childNodes;
32818             for(var i = 0, len = cs.length; i < len; i++){
32819                 cs[i].renderIndent(true, refresh);
32820             }
32821         }
32822     }
32823 });/*
32824  * Based on:
32825  * Ext JS Library 1.1.1
32826  * Copyright(c) 2006-2007, Ext JS, LLC.
32827  *
32828  * Originally Released Under LGPL - original licence link has changed is not relivant.
32829  *
32830  * Fork - LGPL
32831  * <script type="text/javascript">
32832  */
32833  
32834 /**
32835  * @class Roo.tree.AsyncTreeNode
32836  * @extends Roo.tree.TreeNode
32837  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32838  * @constructor
32839  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32840  */
32841  Roo.tree.AsyncTreeNode = function(config){
32842     this.loaded = false;
32843     this.loading = false;
32844     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32845     /**
32846     * @event beforeload
32847     * Fires before this node is loaded, return false to cancel
32848     * @param {Node} this This node
32849     */
32850     this.addEvents({'beforeload':true, 'load': true});
32851     /**
32852     * @event load
32853     * Fires when this node is loaded
32854     * @param {Node} this This node
32855     */
32856     /**
32857      * The loader used by this node (defaults to using the tree's defined loader)
32858      * @type TreeLoader
32859      * @property loader
32860      */
32861 };
32862 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32863     expand : function(deep, anim, callback){
32864         if(this.loading){ // if an async load is already running, waiting til it's done
32865             var timer;
32866             var f = function(){
32867                 if(!this.loading){ // done loading
32868                     clearInterval(timer);
32869                     this.expand(deep, anim, callback);
32870                 }
32871             }.createDelegate(this);
32872             timer = setInterval(f, 200);
32873             return;
32874         }
32875         if(!this.loaded){
32876             if(this.fireEvent("beforeload", this) === false){
32877                 return;
32878             }
32879             this.loading = true;
32880             this.ui.beforeLoad(this);
32881             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32882             if(loader){
32883                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32884                 return;
32885             }
32886         }
32887         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32888     },
32889     
32890     /**
32891      * Returns true if this node is currently loading
32892      * @return {Boolean}
32893      */
32894     isLoading : function(){
32895         return this.loading;  
32896     },
32897     
32898     loadComplete : function(deep, anim, callback){
32899         this.loading = false;
32900         this.loaded = true;
32901         this.ui.afterLoad(this);
32902         this.fireEvent("load", this);
32903         this.expand(deep, anim, callback);
32904     },
32905     
32906     /**
32907      * Returns true if this node has been loaded
32908      * @return {Boolean}
32909      */
32910     isLoaded : function(){
32911         return this.loaded;
32912     },
32913     
32914     hasChildNodes : function(){
32915         if(!this.isLeaf() && !this.loaded){
32916             return true;
32917         }else{
32918             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32919         }
32920     },
32921
32922     /**
32923      * Trigger a reload for this node
32924      * @param {Function} callback
32925      */
32926     reload : function(callback){
32927         this.collapse(false, false);
32928         while(this.firstChild){
32929             this.removeChild(this.firstChild);
32930         }
32931         this.childrenRendered = false;
32932         this.loaded = false;
32933         if(this.isHiddenRoot()){
32934             this.expanded = false;
32935         }
32936         this.expand(false, false, callback);
32937     }
32938 });/*
32939  * Based on:
32940  * Ext JS Library 1.1.1
32941  * Copyright(c) 2006-2007, Ext JS, LLC.
32942  *
32943  * Originally Released Under LGPL - original licence link has changed is not relivant.
32944  *
32945  * Fork - LGPL
32946  * <script type="text/javascript">
32947  */
32948  
32949 /**
32950  * @class Roo.tree.TreeNodeUI
32951  * @constructor
32952  * @param {Object} node The node to render
32953  * The TreeNode UI implementation is separate from the
32954  * tree implementation. Unless you are customizing the tree UI,
32955  * you should never have to use this directly.
32956  */
32957 Roo.tree.TreeNodeUI = function(node){
32958     this.node = node;
32959     this.rendered = false;
32960     this.animating = false;
32961     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32962 };
32963
32964 Roo.tree.TreeNodeUI.prototype = {
32965     removeChild : function(node){
32966         if(this.rendered){
32967             this.ctNode.removeChild(node.ui.getEl());
32968         }
32969     },
32970
32971     beforeLoad : function(){
32972          this.addClass("x-tree-node-loading");
32973     },
32974
32975     afterLoad : function(){
32976          this.removeClass("x-tree-node-loading");
32977     },
32978
32979     onTextChange : function(node, text, oldText){
32980         if(this.rendered){
32981             this.textNode.innerHTML = text;
32982         }
32983     },
32984
32985     onDisableChange : function(node, state){
32986         this.disabled = state;
32987         if(state){
32988             this.addClass("x-tree-node-disabled");
32989         }else{
32990             this.removeClass("x-tree-node-disabled");
32991         }
32992     },
32993
32994     onSelectedChange : function(state){
32995         if(state){
32996             this.focus();
32997             this.addClass("x-tree-selected");
32998         }else{
32999             //this.blur();
33000             this.removeClass("x-tree-selected");
33001         }
33002     },
33003
33004     onMove : function(tree, node, oldParent, newParent, index, refNode){
33005         this.childIndent = null;
33006         if(this.rendered){
33007             var targetNode = newParent.ui.getContainer();
33008             if(!targetNode){//target not rendered
33009                 this.holder = document.createElement("div");
33010                 this.holder.appendChild(this.wrap);
33011                 return;
33012             }
33013             var insertBefore = refNode ? refNode.ui.getEl() : null;
33014             if(insertBefore){
33015                 targetNode.insertBefore(this.wrap, insertBefore);
33016             }else{
33017                 targetNode.appendChild(this.wrap);
33018             }
33019             this.node.renderIndent(true);
33020         }
33021     },
33022
33023     addClass : function(cls){
33024         if(this.elNode){
33025             Roo.fly(this.elNode).addClass(cls);
33026         }
33027     },
33028
33029     removeClass : function(cls){
33030         if(this.elNode){
33031             Roo.fly(this.elNode).removeClass(cls);
33032         }
33033     },
33034
33035     remove : function(){
33036         if(this.rendered){
33037             this.holder = document.createElement("div");
33038             this.holder.appendChild(this.wrap);
33039         }
33040     },
33041
33042     fireEvent : function(){
33043         return this.node.fireEvent.apply(this.node, arguments);
33044     },
33045
33046     initEvents : function(){
33047         this.node.on("move", this.onMove, this);
33048         var E = Roo.EventManager;
33049         var a = this.anchor;
33050
33051         var el = Roo.fly(a, '_treeui');
33052
33053         if(Roo.isOpera){ // opera render bug ignores the CSS
33054             el.setStyle("text-decoration", "none");
33055         }
33056
33057         el.on("click", this.onClick, this);
33058         el.on("dblclick", this.onDblClick, this);
33059
33060         if(this.checkbox){
33061             Roo.EventManager.on(this.checkbox,
33062                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33063         }
33064
33065         el.on("contextmenu", this.onContextMenu, this);
33066
33067         var icon = Roo.fly(this.iconNode);
33068         icon.on("click", this.onClick, this);
33069         icon.on("dblclick", this.onDblClick, this);
33070         icon.on("contextmenu", this.onContextMenu, this);
33071         E.on(this.ecNode, "click", this.ecClick, this, true);
33072
33073         if(this.node.disabled){
33074             this.addClass("x-tree-node-disabled");
33075         }
33076         if(this.node.hidden){
33077             this.addClass("x-tree-node-disabled");
33078         }
33079         var ot = this.node.getOwnerTree();
33080         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33081         if(dd && (!this.node.isRoot || ot.rootVisible)){
33082             Roo.dd.Registry.register(this.elNode, {
33083                 node: this.node,
33084                 handles: this.getDDHandles(),
33085                 isHandle: false
33086             });
33087         }
33088     },
33089
33090     getDDHandles : function(){
33091         return [this.iconNode, this.textNode];
33092     },
33093
33094     hide : function(){
33095         if(this.rendered){
33096             this.wrap.style.display = "none";
33097         }
33098     },
33099
33100     show : function(){
33101         if(this.rendered){
33102             this.wrap.style.display = "";
33103         }
33104     },
33105
33106     onContextMenu : function(e){
33107         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33108             e.preventDefault();
33109             this.focus();
33110             this.fireEvent("contextmenu", this.node, e);
33111         }
33112     },
33113
33114     onClick : function(e){
33115         if(this.dropping){
33116             e.stopEvent();
33117             return;
33118         }
33119         if(this.fireEvent("beforeclick", this.node, e) !== false){
33120             if(!this.disabled && this.node.attributes.href){
33121                 this.fireEvent("click", this.node, e);
33122                 return;
33123             }
33124             e.preventDefault();
33125             if(this.disabled){
33126                 return;
33127             }
33128
33129             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33130                 this.node.toggle();
33131             }
33132
33133             this.fireEvent("click", this.node, e);
33134         }else{
33135             e.stopEvent();
33136         }
33137     },
33138
33139     onDblClick : function(e){
33140         e.preventDefault();
33141         if(this.disabled){
33142             return;
33143         }
33144         if(this.checkbox){
33145             this.toggleCheck();
33146         }
33147         if(!this.animating && this.node.hasChildNodes()){
33148             this.node.toggle();
33149         }
33150         this.fireEvent("dblclick", this.node, e);
33151     },
33152
33153     onCheckChange : function(){
33154         var checked = this.checkbox.checked;
33155         this.node.attributes.checked = checked;
33156         this.fireEvent('checkchange', this.node, checked);
33157     },
33158
33159     ecClick : function(e){
33160         if(!this.animating && this.node.hasChildNodes()){
33161             this.node.toggle();
33162         }
33163     },
33164
33165     startDrop : function(){
33166         this.dropping = true;
33167     },
33168
33169     // delayed drop so the click event doesn't get fired on a drop
33170     endDrop : function(){
33171        setTimeout(function(){
33172            this.dropping = false;
33173        }.createDelegate(this), 50);
33174     },
33175
33176     expand : function(){
33177         this.updateExpandIcon();
33178         this.ctNode.style.display = "";
33179     },
33180
33181     focus : function(){
33182         if(!this.node.preventHScroll){
33183             try{this.anchor.focus();
33184             }catch(e){}
33185         }else if(!Roo.isIE){
33186             try{
33187                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33188                 var l = noscroll.scrollLeft;
33189                 this.anchor.focus();
33190                 noscroll.scrollLeft = l;
33191             }catch(e){}
33192         }
33193     },
33194
33195     toggleCheck : function(value){
33196         var cb = this.checkbox;
33197         if(cb){
33198             cb.checked = (value === undefined ? !cb.checked : value);
33199         }
33200     },
33201
33202     blur : function(){
33203         try{
33204             this.anchor.blur();
33205         }catch(e){}
33206     },
33207
33208     animExpand : function(callback){
33209         var ct = Roo.get(this.ctNode);
33210         ct.stopFx();
33211         if(!this.node.hasChildNodes()){
33212             this.updateExpandIcon();
33213             this.ctNode.style.display = "";
33214             Roo.callback(callback);
33215             return;
33216         }
33217         this.animating = true;
33218         this.updateExpandIcon();
33219
33220         ct.slideIn('t', {
33221            callback : function(){
33222                this.animating = false;
33223                Roo.callback(callback);
33224             },
33225             scope: this,
33226             duration: this.node.ownerTree.duration || .25
33227         });
33228     },
33229
33230     highlight : function(){
33231         var tree = this.node.getOwnerTree();
33232         Roo.fly(this.wrap).highlight(
33233             tree.hlColor || "C3DAF9",
33234             {endColor: tree.hlBaseColor}
33235         );
33236     },
33237
33238     collapse : function(){
33239         this.updateExpandIcon();
33240         this.ctNode.style.display = "none";
33241     },
33242
33243     animCollapse : function(callback){
33244         var ct = Roo.get(this.ctNode);
33245         ct.enableDisplayMode('block');
33246         ct.stopFx();
33247
33248         this.animating = true;
33249         this.updateExpandIcon();
33250
33251         ct.slideOut('t', {
33252             callback : function(){
33253                this.animating = false;
33254                Roo.callback(callback);
33255             },
33256             scope: this,
33257             duration: this.node.ownerTree.duration || .25
33258         });
33259     },
33260
33261     getContainer : function(){
33262         return this.ctNode;
33263     },
33264
33265     getEl : function(){
33266         return this.wrap;
33267     },
33268
33269     appendDDGhost : function(ghostNode){
33270         ghostNode.appendChild(this.elNode.cloneNode(true));
33271     },
33272
33273     getDDRepairXY : function(){
33274         return Roo.lib.Dom.getXY(this.iconNode);
33275     },
33276
33277     onRender : function(){
33278         this.render();
33279     },
33280
33281     render : function(bulkRender){
33282         var n = this.node, a = n.attributes;
33283         var targetNode = n.parentNode ?
33284               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33285
33286         if(!this.rendered){
33287             this.rendered = true;
33288
33289             this.renderElements(n, a, targetNode, bulkRender);
33290
33291             if(a.qtip){
33292                if(this.textNode.setAttributeNS){
33293                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33294                    if(a.qtipTitle){
33295                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33296                    }
33297                }else{
33298                    this.textNode.setAttribute("ext:qtip", a.qtip);
33299                    if(a.qtipTitle){
33300                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33301                    }
33302                }
33303             }else if(a.qtipCfg){
33304                 a.qtipCfg.target = Roo.id(this.textNode);
33305                 Roo.QuickTips.register(a.qtipCfg);
33306             }
33307             this.initEvents();
33308             if(!this.node.expanded){
33309                 this.updateExpandIcon();
33310             }
33311         }else{
33312             if(bulkRender === true) {
33313                 targetNode.appendChild(this.wrap);
33314             }
33315         }
33316     },
33317
33318     renderElements : function(n, a, targetNode, bulkRender)
33319     {
33320         // add some indent caching, this helps performance when rendering a large tree
33321         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33322         var t = n.getOwnerTree();
33323         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33324         if (typeof(n.attributes.html) != 'undefined') {
33325             txt = n.attributes.html;
33326         }
33327         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33328         var cb = typeof a.checked == 'boolean';
33329         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33330         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33331             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33332             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33333             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33334             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33335             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33336              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33337                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33338             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33339             "</li>"];
33340
33341         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33342             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33343                                 n.nextSibling.ui.getEl(), buf.join(""));
33344         }else{
33345             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33346         }
33347
33348         this.elNode = this.wrap.childNodes[0];
33349         this.ctNode = this.wrap.childNodes[1];
33350         var cs = this.elNode.childNodes;
33351         this.indentNode = cs[0];
33352         this.ecNode = cs[1];
33353         this.iconNode = cs[2];
33354         var index = 3;
33355         if(cb){
33356             this.checkbox = cs[3];
33357             index++;
33358         }
33359         this.anchor = cs[index];
33360         this.textNode = cs[index].firstChild;
33361     },
33362
33363     getAnchor : function(){
33364         return this.anchor;
33365     },
33366
33367     getTextEl : function(){
33368         return this.textNode;
33369     },
33370
33371     getIconEl : function(){
33372         return this.iconNode;
33373     },
33374
33375     isChecked : function(){
33376         return this.checkbox ? this.checkbox.checked : false;
33377     },
33378
33379     updateExpandIcon : function(){
33380         if(this.rendered){
33381             var n = this.node, c1, c2;
33382             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
33383             var hasChild = n.hasChildNodes();
33384             if(hasChild){
33385                 if(n.expanded){
33386                     cls += "-minus";
33387                     c1 = "x-tree-node-collapsed";
33388                     c2 = "x-tree-node-expanded";
33389                 }else{
33390                     cls += "-plus";
33391                     c1 = "x-tree-node-expanded";
33392                     c2 = "x-tree-node-collapsed";
33393                 }
33394                 if(this.wasLeaf){
33395                     this.removeClass("x-tree-node-leaf");
33396                     this.wasLeaf = false;
33397                 }
33398                 if(this.c1 != c1 || this.c2 != c2){
33399                     Roo.fly(this.elNode).replaceClass(c1, c2);
33400                     this.c1 = c1; this.c2 = c2;
33401                 }
33402             }else{
33403                 // this changes non-leafs into leafs if they have no children.
33404                 // it's not very rational behaviour..
33405                 
33406                 if(!this.wasLeaf && this.node.leaf){
33407                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
33408                     delete this.c1;
33409                     delete this.c2;
33410                     this.wasLeaf = true;
33411                 }
33412             }
33413             var ecc = "x-tree-ec-icon "+cls;
33414             if(this.ecc != ecc){
33415                 this.ecNode.className = ecc;
33416                 this.ecc = ecc;
33417             }
33418         }
33419     },
33420
33421     getChildIndent : function(){
33422         if(!this.childIndent){
33423             var buf = [];
33424             var p = this.node;
33425             while(p){
33426                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
33427                     if(!p.isLast()) {
33428                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
33429                     } else {
33430                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
33431                     }
33432                 }
33433                 p = p.parentNode;
33434             }
33435             this.childIndent = buf.join("");
33436         }
33437         return this.childIndent;
33438     },
33439
33440     renderIndent : function(){
33441         if(this.rendered){
33442             var indent = "";
33443             var p = this.node.parentNode;
33444             if(p){
33445                 indent = p.ui.getChildIndent();
33446             }
33447             if(this.indentMarkup != indent){ // don't rerender if not required
33448                 this.indentNode.innerHTML = indent;
33449                 this.indentMarkup = indent;
33450             }
33451             this.updateExpandIcon();
33452         }
33453     }
33454 };
33455
33456 Roo.tree.RootTreeNodeUI = function(){
33457     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
33458 };
33459 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
33460     render : function(){
33461         if(!this.rendered){
33462             var targetNode = this.node.ownerTree.innerCt.dom;
33463             this.node.expanded = true;
33464             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
33465             this.wrap = this.ctNode = targetNode.firstChild;
33466         }
33467     },
33468     collapse : function(){
33469     },
33470     expand : function(){
33471     }
33472 });/*
33473  * Based on:
33474  * Ext JS Library 1.1.1
33475  * Copyright(c) 2006-2007, Ext JS, LLC.
33476  *
33477  * Originally Released Under LGPL - original licence link has changed is not relivant.
33478  *
33479  * Fork - LGPL
33480  * <script type="text/javascript">
33481  */
33482 /**
33483  * @class Roo.tree.TreeLoader
33484  * @extends Roo.util.Observable
33485  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
33486  * nodes from a specified URL. The response must be a javascript Array definition
33487  * who's elements are node definition objects. eg:
33488  * <pre><code>
33489 {  success : true,
33490    data :      [
33491    
33492     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
33493     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
33494     ]
33495 }
33496
33497
33498 </code></pre>
33499  * <br><br>
33500  * The old style respose with just an array is still supported, but not recommended.
33501  * <br><br>
33502  *
33503  * A server request is sent, and child nodes are loaded only when a node is expanded.
33504  * The loading node's id is passed to the server under the parameter name "node" to
33505  * enable the server to produce the correct child nodes.
33506  * <br><br>
33507  * To pass extra parameters, an event handler may be attached to the "beforeload"
33508  * event, and the parameters specified in the TreeLoader's baseParams property:
33509  * <pre><code>
33510     myTreeLoader.on("beforeload", function(treeLoader, node) {
33511         this.baseParams.category = node.attributes.category;
33512     }, this);
33513 </code></pre><
33514  * This would pass an HTTP parameter called "category" to the server containing
33515  * the value of the Node's "category" attribute.
33516  * @constructor
33517  * Creates a new Treeloader.
33518  * @param {Object} config A config object containing config properties.
33519  */
33520 Roo.tree.TreeLoader = function(config){
33521     this.baseParams = {};
33522     this.requestMethod = "POST";
33523     Roo.apply(this, config);
33524
33525     this.addEvents({
33526     
33527         /**
33528          * @event beforeload
33529          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
33530          * @param {Object} This TreeLoader object.
33531          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33532          * @param {Object} callback The callback function specified in the {@link #load} call.
33533          */
33534         beforeload : true,
33535         /**
33536          * @event load
33537          * Fires when the node has been successfuly loaded.
33538          * @param {Object} This TreeLoader object.
33539          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33540          * @param {Object} response The response object containing the data from the server.
33541          */
33542         load : true,
33543         /**
33544          * @event loadexception
33545          * Fires if the network request failed.
33546          * @param {Object} This TreeLoader object.
33547          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33548          * @param {Object} response The response object containing the data from the server.
33549          */
33550         loadexception : true,
33551         /**
33552          * @event create
33553          * Fires before a node is created, enabling you to return custom Node types 
33554          * @param {Object} This TreeLoader object.
33555          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
33556          */
33557         create : true
33558     });
33559
33560     Roo.tree.TreeLoader.superclass.constructor.call(this);
33561 };
33562
33563 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
33564     /**
33565     * @cfg {String} dataUrl The URL from which to request a Json string which
33566     * specifies an array of node definition object representing the child nodes
33567     * to be loaded.
33568     */
33569     /**
33570     * @cfg {String} requestMethod either GET or POST
33571     * defaults to POST (due to BC)
33572     * to be loaded.
33573     */
33574     /**
33575     * @cfg {Object} baseParams (optional) An object containing properties which
33576     * specify HTTP parameters to be passed to each request for child nodes.
33577     */
33578     /**
33579     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
33580     * created by this loader. If the attributes sent by the server have an attribute in this object,
33581     * they take priority.
33582     */
33583     /**
33584     * @cfg {Object} uiProviders (optional) An object containing properties which
33585     * 
33586     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
33587     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
33588     * <i>uiProvider</i> attribute of a returned child node is a string rather
33589     * than a reference to a TreeNodeUI implementation, this that string value
33590     * is used as a property name in the uiProviders object. You can define the provider named
33591     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
33592     */
33593     uiProviders : {},
33594
33595     /**
33596     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
33597     * child nodes before loading.
33598     */
33599     clearOnLoad : true,
33600
33601     /**
33602     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
33603     * property on loading, rather than expecting an array. (eg. more compatible to a standard
33604     * Grid query { data : [ .....] }
33605     */
33606     
33607     root : false,
33608      /**
33609     * @cfg {String} queryParam (optional) 
33610     * Name of the query as it will be passed on the querystring (defaults to 'node')
33611     * eg. the request will be ?node=[id]
33612     */
33613     
33614     
33615     queryParam: false,
33616     
33617     /**
33618      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
33619      * This is called automatically when a node is expanded, but may be used to reload
33620      * a node (or append new children if the {@link #clearOnLoad} option is false.)
33621      * @param {Roo.tree.TreeNode} node
33622      * @param {Function} callback
33623      */
33624     load : function(node, callback){
33625         if(this.clearOnLoad){
33626             while(node.firstChild){
33627                 node.removeChild(node.firstChild);
33628             }
33629         }
33630         if(node.attributes.children){ // preloaded json children
33631             var cs = node.attributes.children;
33632             for(var i = 0, len = cs.length; i < len; i++){
33633                 node.appendChild(this.createNode(cs[i]));
33634             }
33635             if(typeof callback == "function"){
33636                 callback();
33637             }
33638         }else if(this.dataUrl){
33639             this.requestData(node, callback);
33640         }
33641     },
33642
33643     getParams: function(node){
33644         var buf = [], bp = this.baseParams;
33645         for(var key in bp){
33646             if(typeof bp[key] != "function"){
33647                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
33648             }
33649         }
33650         var n = this.queryParam === false ? 'node' : this.queryParam;
33651         buf.push(n + "=", encodeURIComponent(node.id));
33652         return buf.join("");
33653     },
33654
33655     requestData : function(node, callback){
33656         if(this.fireEvent("beforeload", this, node, callback) !== false){
33657             this.transId = Roo.Ajax.request({
33658                 method:this.requestMethod,
33659                 url: this.dataUrl||this.url,
33660                 success: this.handleResponse,
33661                 failure: this.handleFailure,
33662                 scope: this,
33663                 argument: {callback: callback, node: node},
33664                 params: this.getParams(node)
33665             });
33666         }else{
33667             // if the load is cancelled, make sure we notify
33668             // the node that we are done
33669             if(typeof callback == "function"){
33670                 callback();
33671             }
33672         }
33673     },
33674
33675     isLoading : function(){
33676         return this.transId ? true : false;
33677     },
33678
33679     abort : function(){
33680         if(this.isLoading()){
33681             Roo.Ajax.abort(this.transId);
33682         }
33683     },
33684
33685     // private
33686     createNode : function(attr)
33687     {
33688         // apply baseAttrs, nice idea Corey!
33689         if(this.baseAttrs){
33690             Roo.applyIf(attr, this.baseAttrs);
33691         }
33692         if(this.applyLoader !== false){
33693             attr.loader = this;
33694         }
33695         // uiProvider = depreciated..
33696         
33697         if(typeof(attr.uiProvider) == 'string'){
33698            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33699                 /**  eval:var:attr */ eval(attr.uiProvider);
33700         }
33701         if(typeof(this.uiProviders['default']) != 'undefined') {
33702             attr.uiProvider = this.uiProviders['default'];
33703         }
33704         
33705         this.fireEvent('create', this, attr);
33706         
33707         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33708         return(attr.leaf ?
33709                         new Roo.tree.TreeNode(attr) :
33710                         new Roo.tree.AsyncTreeNode(attr));
33711     },
33712
33713     processResponse : function(response, node, callback)
33714     {
33715         var json = response.responseText;
33716         try {
33717             
33718             var o = Roo.decode(json);
33719             
33720             if (this.root === false && typeof(o.success) != undefined) {
33721                 this.root = 'data'; // the default behaviour for list like data..
33722                 }
33723                 
33724             if (this.root !== false &&  !o.success) {
33725                 // it's a failure condition.
33726                 var a = response.argument;
33727                 this.fireEvent("loadexception", this, a.node, response);
33728                 Roo.log("Load failed - should have a handler really");
33729                 return;
33730             }
33731             
33732             
33733             
33734             if (this.root !== false) {
33735                  o = o[this.root];
33736             }
33737             
33738             for(var i = 0, len = o.length; i < len; i++){
33739                 var n = this.createNode(o[i]);
33740                 if(n){
33741                     node.appendChild(n);
33742                 }
33743             }
33744             if(typeof callback == "function"){
33745                 callback(this, node);
33746             }
33747         }catch(e){
33748             this.handleFailure(response);
33749         }
33750     },
33751
33752     handleResponse : function(response){
33753         this.transId = false;
33754         var a = response.argument;
33755         this.processResponse(response, a.node, a.callback);
33756         this.fireEvent("load", this, a.node, response);
33757     },
33758
33759     handleFailure : function(response)
33760     {
33761         // should handle failure better..
33762         this.transId = false;
33763         var a = response.argument;
33764         this.fireEvent("loadexception", this, a.node, response);
33765         if(typeof a.callback == "function"){
33766             a.callback(this, a.node);
33767         }
33768     }
33769 });/*
33770  * Based on:
33771  * Ext JS Library 1.1.1
33772  * Copyright(c) 2006-2007, Ext JS, LLC.
33773  *
33774  * Originally Released Under LGPL - original licence link has changed is not relivant.
33775  *
33776  * Fork - LGPL
33777  * <script type="text/javascript">
33778  */
33779
33780 /**
33781 * @class Roo.tree.TreeFilter
33782 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33783 * @param {TreePanel} tree
33784 * @param {Object} config (optional)
33785  */
33786 Roo.tree.TreeFilter = function(tree, config){
33787     this.tree = tree;
33788     this.filtered = {};
33789     Roo.apply(this, config);
33790 };
33791
33792 Roo.tree.TreeFilter.prototype = {
33793     clearBlank:false,
33794     reverse:false,
33795     autoClear:false,
33796     remove:false,
33797
33798      /**
33799      * Filter the data by a specific attribute.
33800      * @param {String/RegExp} value Either string that the attribute value
33801      * should start with or a RegExp to test against the attribute
33802      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33803      * @param {TreeNode} startNode (optional) The node to start the filter at.
33804      */
33805     filter : function(value, attr, startNode){
33806         attr = attr || "text";
33807         var f;
33808         if(typeof value == "string"){
33809             var vlen = value.length;
33810             // auto clear empty filter
33811             if(vlen == 0 && this.clearBlank){
33812                 this.clear();
33813                 return;
33814             }
33815             value = value.toLowerCase();
33816             f = function(n){
33817                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33818             };
33819         }else if(value.exec){ // regex?
33820             f = function(n){
33821                 return value.test(n.attributes[attr]);
33822             };
33823         }else{
33824             throw 'Illegal filter type, must be string or regex';
33825         }
33826         this.filterBy(f, null, startNode);
33827         },
33828
33829     /**
33830      * Filter by a function. The passed function will be called with each
33831      * node in the tree (or from the startNode). If the function returns true, the node is kept
33832      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33833      * @param {Function} fn The filter function
33834      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33835      */
33836     filterBy : function(fn, scope, startNode){
33837         startNode = startNode || this.tree.root;
33838         if(this.autoClear){
33839             this.clear();
33840         }
33841         var af = this.filtered, rv = this.reverse;
33842         var f = function(n){
33843             if(n == startNode){
33844                 return true;
33845             }
33846             if(af[n.id]){
33847                 return false;
33848             }
33849             var m = fn.call(scope || n, n);
33850             if(!m || rv){
33851                 af[n.id] = n;
33852                 n.ui.hide();
33853                 return false;
33854             }
33855             return true;
33856         };
33857         startNode.cascade(f);
33858         if(this.remove){
33859            for(var id in af){
33860                if(typeof id != "function"){
33861                    var n = af[id];
33862                    if(n && n.parentNode){
33863                        n.parentNode.removeChild(n);
33864                    }
33865                }
33866            }
33867         }
33868     },
33869
33870     /**
33871      * Clears the current filter. Note: with the "remove" option
33872      * set a filter cannot be cleared.
33873      */
33874     clear : function(){
33875         var t = this.tree;
33876         var af = this.filtered;
33877         for(var id in af){
33878             if(typeof id != "function"){
33879                 var n = af[id];
33880                 if(n){
33881                     n.ui.show();
33882                 }
33883             }
33884         }
33885         this.filtered = {};
33886     }
33887 };
33888 /*
33889  * Based on:
33890  * Ext JS Library 1.1.1
33891  * Copyright(c) 2006-2007, Ext JS, LLC.
33892  *
33893  * Originally Released Under LGPL - original licence link has changed is not relivant.
33894  *
33895  * Fork - LGPL
33896  * <script type="text/javascript">
33897  */
33898  
33899
33900 /**
33901  * @class Roo.tree.TreeSorter
33902  * Provides sorting of nodes in a TreePanel
33903  * 
33904  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33905  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33906  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33907  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33908  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33909  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33910  * @constructor
33911  * @param {TreePanel} tree
33912  * @param {Object} config
33913  */
33914 Roo.tree.TreeSorter = function(tree, config){
33915     Roo.apply(this, config);
33916     tree.on("beforechildrenrendered", this.doSort, this);
33917     tree.on("append", this.updateSort, this);
33918     tree.on("insert", this.updateSort, this);
33919     
33920     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33921     var p = this.property || "text";
33922     var sortType = this.sortType;
33923     var fs = this.folderSort;
33924     var cs = this.caseSensitive === true;
33925     var leafAttr = this.leafAttr || 'leaf';
33926
33927     this.sortFn = function(n1, n2){
33928         if(fs){
33929             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33930                 return 1;
33931             }
33932             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33933                 return -1;
33934             }
33935         }
33936         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33937         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33938         if(v1 < v2){
33939                         return dsc ? +1 : -1;
33940                 }else if(v1 > v2){
33941                         return dsc ? -1 : +1;
33942         }else{
33943                 return 0;
33944         }
33945     };
33946 };
33947
33948 Roo.tree.TreeSorter.prototype = {
33949     doSort : function(node){
33950         node.sort(this.sortFn);
33951     },
33952     
33953     compareNodes : function(n1, n2){
33954         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33955     },
33956     
33957     updateSort : function(tree, node){
33958         if(node.childrenRendered){
33959             this.doSort.defer(1, this, [node]);
33960         }
33961     }
33962 };/*
33963  * Based on:
33964  * Ext JS Library 1.1.1
33965  * Copyright(c) 2006-2007, Ext JS, LLC.
33966  *
33967  * Originally Released Under LGPL - original licence link has changed is not relivant.
33968  *
33969  * Fork - LGPL
33970  * <script type="text/javascript">
33971  */
33972
33973 if(Roo.dd.DropZone){
33974     
33975 Roo.tree.TreeDropZone = function(tree, config){
33976     this.allowParentInsert = false;
33977     this.allowContainerDrop = false;
33978     this.appendOnly = false;
33979     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33980     this.tree = tree;
33981     this.lastInsertClass = "x-tree-no-status";
33982     this.dragOverData = {};
33983 };
33984
33985 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33986     ddGroup : "TreeDD",
33987     scroll:  true,
33988     
33989     expandDelay : 1000,
33990     
33991     expandNode : function(node){
33992         if(node.hasChildNodes() && !node.isExpanded()){
33993             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33994         }
33995     },
33996     
33997     queueExpand : function(node){
33998         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33999     },
34000     
34001     cancelExpand : function(){
34002         if(this.expandProcId){
34003             clearTimeout(this.expandProcId);
34004             this.expandProcId = false;
34005         }
34006     },
34007     
34008     isValidDropPoint : function(n, pt, dd, e, data){
34009         if(!n || !data){ return false; }
34010         var targetNode = n.node;
34011         var dropNode = data.node;
34012         // default drop rules
34013         if(!(targetNode && targetNode.isTarget && pt)){
34014             return false;
34015         }
34016         if(pt == "append" && targetNode.allowChildren === false){
34017             return false;
34018         }
34019         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34020             return false;
34021         }
34022         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34023             return false;
34024         }
34025         // reuse the object
34026         var overEvent = this.dragOverData;
34027         overEvent.tree = this.tree;
34028         overEvent.target = targetNode;
34029         overEvent.data = data;
34030         overEvent.point = pt;
34031         overEvent.source = dd;
34032         overEvent.rawEvent = e;
34033         overEvent.dropNode = dropNode;
34034         overEvent.cancel = false;  
34035         var result = this.tree.fireEvent("nodedragover", overEvent);
34036         return overEvent.cancel === false && result !== false;
34037     },
34038     
34039     getDropPoint : function(e, n, dd)
34040     {
34041         var tn = n.node;
34042         if(tn.isRoot){
34043             return tn.allowChildren !== false ? "append" : false; // always append for root
34044         }
34045         var dragEl = n.ddel;
34046         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34047         var y = Roo.lib.Event.getPageY(e);
34048         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34049         
34050         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34051         var noAppend = tn.allowChildren === false;
34052         if(this.appendOnly || tn.parentNode.allowChildren === false){
34053             return noAppend ? false : "append";
34054         }
34055         var noBelow = false;
34056         if(!this.allowParentInsert){
34057             noBelow = tn.hasChildNodes() && tn.isExpanded();
34058         }
34059         var q = (b - t) / (noAppend ? 2 : 3);
34060         if(y >= t && y < (t + q)){
34061             return "above";
34062         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34063             return "below";
34064         }else{
34065             return "append";
34066         }
34067     },
34068     
34069     onNodeEnter : function(n, dd, e, data)
34070     {
34071         this.cancelExpand();
34072     },
34073     
34074     onNodeOver : function(n, dd, e, data)
34075     {
34076        
34077         var pt = this.getDropPoint(e, n, dd);
34078         var node = n.node;
34079         
34080         // auto node expand check
34081         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34082             this.queueExpand(node);
34083         }else if(pt != "append"){
34084             this.cancelExpand();
34085         }
34086         
34087         // set the insert point style on the target node
34088         var returnCls = this.dropNotAllowed;
34089         if(this.isValidDropPoint(n, pt, dd, e, data)){
34090            if(pt){
34091                var el = n.ddel;
34092                var cls;
34093                if(pt == "above"){
34094                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34095                    cls = "x-tree-drag-insert-above";
34096                }else if(pt == "below"){
34097                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34098                    cls = "x-tree-drag-insert-below";
34099                }else{
34100                    returnCls = "x-tree-drop-ok-append";
34101                    cls = "x-tree-drag-append";
34102                }
34103                if(this.lastInsertClass != cls){
34104                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34105                    this.lastInsertClass = cls;
34106                }
34107            }
34108        }
34109        return returnCls;
34110     },
34111     
34112     onNodeOut : function(n, dd, e, data){
34113         
34114         this.cancelExpand();
34115         this.removeDropIndicators(n);
34116     },
34117     
34118     onNodeDrop : function(n, dd, e, data){
34119         var point = this.getDropPoint(e, n, dd);
34120         var targetNode = n.node;
34121         targetNode.ui.startDrop();
34122         if(!this.isValidDropPoint(n, point, dd, e, data)){
34123             targetNode.ui.endDrop();
34124             return false;
34125         }
34126         // first try to find the drop node
34127         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34128         var dropEvent = {
34129             tree : this.tree,
34130             target: targetNode,
34131             data: data,
34132             point: point,
34133             source: dd,
34134             rawEvent: e,
34135             dropNode: dropNode,
34136             cancel: !dropNode   
34137         };
34138         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34139         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34140             targetNode.ui.endDrop();
34141             return false;
34142         }
34143         // allow target changing
34144         targetNode = dropEvent.target;
34145         if(point == "append" && !targetNode.isExpanded()){
34146             targetNode.expand(false, null, function(){
34147                 this.completeDrop(dropEvent);
34148             }.createDelegate(this));
34149         }else{
34150             this.completeDrop(dropEvent);
34151         }
34152         return true;
34153     },
34154     
34155     completeDrop : function(de){
34156         var ns = de.dropNode, p = de.point, t = de.target;
34157         if(!(ns instanceof Array)){
34158             ns = [ns];
34159         }
34160         var n;
34161         for(var i = 0, len = ns.length; i < len; i++){
34162             n = ns[i];
34163             if(p == "above"){
34164                 t.parentNode.insertBefore(n, t);
34165             }else if(p == "below"){
34166                 t.parentNode.insertBefore(n, t.nextSibling);
34167             }else{
34168                 t.appendChild(n);
34169             }
34170         }
34171         n.ui.focus();
34172         if(this.tree.hlDrop){
34173             n.ui.highlight();
34174         }
34175         t.ui.endDrop();
34176         this.tree.fireEvent("nodedrop", de);
34177     },
34178     
34179     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34180         if(this.tree.hlDrop){
34181             dropNode.ui.focus();
34182             dropNode.ui.highlight();
34183         }
34184         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34185     },
34186     
34187     getTree : function(){
34188         return this.tree;
34189     },
34190     
34191     removeDropIndicators : function(n){
34192         if(n && n.ddel){
34193             var el = n.ddel;
34194             Roo.fly(el).removeClass([
34195                     "x-tree-drag-insert-above",
34196                     "x-tree-drag-insert-below",
34197                     "x-tree-drag-append"]);
34198             this.lastInsertClass = "_noclass";
34199         }
34200     },
34201     
34202     beforeDragDrop : function(target, e, id){
34203         this.cancelExpand();
34204         return true;
34205     },
34206     
34207     afterRepair : function(data){
34208         if(data && Roo.enableFx){
34209             data.node.ui.highlight();
34210         }
34211         this.hideProxy();
34212     } 
34213     
34214 });
34215
34216 }
34217 /*
34218  * Based on:
34219  * Ext JS Library 1.1.1
34220  * Copyright(c) 2006-2007, Ext JS, LLC.
34221  *
34222  * Originally Released Under LGPL - original licence link has changed is not relivant.
34223  *
34224  * Fork - LGPL
34225  * <script type="text/javascript">
34226  */
34227  
34228
34229 if(Roo.dd.DragZone){
34230 Roo.tree.TreeDragZone = function(tree, config){
34231     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34232     this.tree = tree;
34233 };
34234
34235 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34236     ddGroup : "TreeDD",
34237    
34238     onBeforeDrag : function(data, e){
34239         var n = data.node;
34240         return n && n.draggable && !n.disabled;
34241     },
34242      
34243     
34244     onInitDrag : function(e){
34245         var data = this.dragData;
34246         this.tree.getSelectionModel().select(data.node);
34247         this.proxy.update("");
34248         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34249         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34250     },
34251     
34252     getRepairXY : function(e, data){
34253         return data.node.ui.getDDRepairXY();
34254     },
34255     
34256     onEndDrag : function(data, e){
34257         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34258         
34259         
34260     },
34261     
34262     onValidDrop : function(dd, e, id){
34263         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34264         this.hideProxy();
34265     },
34266     
34267     beforeInvalidDrop : function(e, id){
34268         // this scrolls the original position back into view
34269         var sm = this.tree.getSelectionModel();
34270         sm.clearSelections();
34271         sm.select(this.dragData.node);
34272     }
34273 });
34274 }/*
34275  * Based on:
34276  * Ext JS Library 1.1.1
34277  * Copyright(c) 2006-2007, Ext JS, LLC.
34278  *
34279  * Originally Released Under LGPL - original licence link has changed is not relivant.
34280  *
34281  * Fork - LGPL
34282  * <script type="text/javascript">
34283  */
34284 /**
34285  * @class Roo.tree.TreeEditor
34286  * @extends Roo.Editor
34287  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34288  * as the editor field.
34289  * @constructor
34290  * @param {Object} config (used to be the tree panel.)
34291  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34292  * 
34293  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34294  * @cfg {Roo.form.TextField|Object} field The field configuration
34295  *
34296  * 
34297  */
34298 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34299     var tree = config;
34300     var field;
34301     if (oldconfig) { // old style..
34302         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34303     } else {
34304         // new style..
34305         tree = config.tree;
34306         config.field = config.field  || {};
34307         config.field.xtype = 'TextField';
34308         field = Roo.factory(config.field, Roo.form);
34309     }
34310     config = config || {};
34311     
34312     
34313     this.addEvents({
34314         /**
34315          * @event beforenodeedit
34316          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34317          * false from the handler of this event.
34318          * @param {Editor} this
34319          * @param {Roo.tree.Node} node 
34320          */
34321         "beforenodeedit" : true
34322     });
34323     
34324     //Roo.log(config);
34325     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34326
34327     this.tree = tree;
34328
34329     tree.on('beforeclick', this.beforeNodeClick, this);
34330     tree.getTreeEl().on('mousedown', this.hide, this);
34331     this.on('complete', this.updateNode, this);
34332     this.on('beforestartedit', this.fitToTree, this);
34333     this.on('startedit', this.bindScroll, this, {delay:10});
34334     this.on('specialkey', this.onSpecialKey, this);
34335 };
34336
34337 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34338     /**
34339      * @cfg {String} alignment
34340      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34341      */
34342     alignment: "l-l",
34343     // inherit
34344     autoSize: false,
34345     /**
34346      * @cfg {Boolean} hideEl
34347      * True to hide the bound element while the editor is displayed (defaults to false)
34348      */
34349     hideEl : false,
34350     /**
34351      * @cfg {String} cls
34352      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34353      */
34354     cls: "x-small-editor x-tree-editor",
34355     /**
34356      * @cfg {Boolean} shim
34357      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34358      */
34359     shim:false,
34360     // inherit
34361     shadow:"frame",
34362     /**
34363      * @cfg {Number} maxWidth
34364      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34365      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34366      * scroll and client offsets into account prior to each edit.
34367      */
34368     maxWidth: 250,
34369
34370     editDelay : 350,
34371
34372     // private
34373     fitToTree : function(ed, el){
34374         var td = this.tree.getTreeEl().dom, nd = el.dom;
34375         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34376             td.scrollLeft = nd.offsetLeft;
34377         }
34378         var w = Math.min(
34379                 this.maxWidth,
34380                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
34381         this.setSize(w, '');
34382         
34383         return this.fireEvent('beforenodeedit', this, this.editNode);
34384         
34385     },
34386
34387     // private
34388     triggerEdit : function(node){
34389         this.completeEdit();
34390         this.editNode = node;
34391         this.startEdit(node.ui.textNode, node.text);
34392     },
34393
34394     // private
34395     bindScroll : function(){
34396         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
34397     },
34398
34399     // private
34400     beforeNodeClick : function(node, e){
34401         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
34402         this.lastClick = new Date();
34403         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
34404             e.stopEvent();
34405             this.triggerEdit(node);
34406             return false;
34407         }
34408         return true;
34409     },
34410
34411     // private
34412     updateNode : function(ed, value){
34413         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
34414         this.editNode.setText(value);
34415     },
34416
34417     // private
34418     onHide : function(){
34419         Roo.tree.TreeEditor.superclass.onHide.call(this);
34420         if(this.editNode){
34421             this.editNode.ui.focus();
34422         }
34423     },
34424
34425     // private
34426     onSpecialKey : function(field, e){
34427         var k = e.getKey();
34428         if(k == e.ESC){
34429             e.stopEvent();
34430             this.cancelEdit();
34431         }else if(k == e.ENTER && !e.hasModifier()){
34432             e.stopEvent();
34433             this.completeEdit();
34434         }
34435     }
34436 });//<Script type="text/javascript">
34437 /*
34438  * Based on:
34439  * Ext JS Library 1.1.1
34440  * Copyright(c) 2006-2007, Ext JS, LLC.
34441  *
34442  * Originally Released Under LGPL - original licence link has changed is not relivant.
34443  *
34444  * Fork - LGPL
34445  * <script type="text/javascript">
34446  */
34447  
34448 /**
34449  * Not documented??? - probably should be...
34450  */
34451
34452 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
34453     //focus: Roo.emptyFn, // prevent odd scrolling behavior
34454     
34455     renderElements : function(n, a, targetNode, bulkRender){
34456         //consel.log("renderElements?");
34457         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34458
34459         var t = n.getOwnerTree();
34460         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
34461         
34462         var cols = t.columns;
34463         var bw = t.borderWidth;
34464         var c = cols[0];
34465         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34466          var cb = typeof a.checked == "boolean";
34467         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34468         var colcls = 'x-t-' + tid + '-c0';
34469         var buf = [
34470             '<li class="x-tree-node">',
34471             
34472                 
34473                 '<div class="x-tree-node-el ', a.cls,'">',
34474                     // extran...
34475                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
34476                 
34477                 
34478                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
34479                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
34480                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
34481                            (a.icon ? ' x-tree-node-inline-icon' : ''),
34482                            (a.iconCls ? ' '+a.iconCls : ''),
34483                            '" unselectable="on" />',
34484                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
34485                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
34486                              
34487                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34488                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
34489                             '<span unselectable="on" qtip="' + tx + '">',
34490                              tx,
34491                              '</span></a>' ,
34492                     '</div>',
34493                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34494                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
34495                  ];
34496         for(var i = 1, len = cols.length; i < len; i++){
34497             c = cols[i];
34498             colcls = 'x-t-' + tid + '-c' +i;
34499             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34500             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
34501                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
34502                       "</div>");
34503          }
34504          
34505          buf.push(
34506             '</a>',
34507             '<div class="x-clear"></div></div>',
34508             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34509             "</li>");
34510         
34511         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34512             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34513                                 n.nextSibling.ui.getEl(), buf.join(""));
34514         }else{
34515             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34516         }
34517         var el = this.wrap.firstChild;
34518         this.elRow = el;
34519         this.elNode = el.firstChild;
34520         this.ranchor = el.childNodes[1];
34521         this.ctNode = this.wrap.childNodes[1];
34522         var cs = el.firstChild.childNodes;
34523         this.indentNode = cs[0];
34524         this.ecNode = cs[1];
34525         this.iconNode = cs[2];
34526         var index = 3;
34527         if(cb){
34528             this.checkbox = cs[3];
34529             index++;
34530         }
34531         this.anchor = cs[index];
34532         
34533         this.textNode = cs[index].firstChild;
34534         
34535         //el.on("click", this.onClick, this);
34536         //el.on("dblclick", this.onDblClick, this);
34537         
34538         
34539        // console.log(this);
34540     },
34541     initEvents : function(){
34542         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
34543         
34544             
34545         var a = this.ranchor;
34546
34547         var el = Roo.get(a);
34548
34549         if(Roo.isOpera){ // opera render bug ignores the CSS
34550             el.setStyle("text-decoration", "none");
34551         }
34552
34553         el.on("click", this.onClick, this);
34554         el.on("dblclick", this.onDblClick, this);
34555         el.on("contextmenu", this.onContextMenu, this);
34556         
34557     },
34558     
34559     /*onSelectedChange : function(state){
34560         if(state){
34561             this.focus();
34562             this.addClass("x-tree-selected");
34563         }else{
34564             //this.blur();
34565             this.removeClass("x-tree-selected");
34566         }
34567     },*/
34568     addClass : function(cls){
34569         if(this.elRow){
34570             Roo.fly(this.elRow).addClass(cls);
34571         }
34572         
34573     },
34574     
34575     
34576     removeClass : function(cls){
34577         if(this.elRow){
34578             Roo.fly(this.elRow).removeClass(cls);
34579         }
34580     }
34581
34582     
34583     
34584 });//<Script type="text/javascript">
34585
34586 /*
34587  * Based on:
34588  * Ext JS Library 1.1.1
34589  * Copyright(c) 2006-2007, Ext JS, LLC.
34590  *
34591  * Originally Released Under LGPL - original licence link has changed is not relivant.
34592  *
34593  * Fork - LGPL
34594  * <script type="text/javascript">
34595  */
34596  
34597
34598 /**
34599  * @class Roo.tree.ColumnTree
34600  * @extends Roo.data.TreePanel
34601  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
34602  * @cfg {int} borderWidth  compined right/left border allowance
34603  * @constructor
34604  * @param {String/HTMLElement/Element} el The container element
34605  * @param {Object} config
34606  */
34607 Roo.tree.ColumnTree =  function(el, config)
34608 {
34609    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
34610    this.addEvents({
34611         /**
34612         * @event resize
34613         * Fire this event on a container when it resizes
34614         * @param {int} w Width
34615         * @param {int} h Height
34616         */
34617        "resize" : true
34618     });
34619     this.on('resize', this.onResize, this);
34620 };
34621
34622 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
34623     //lines:false,
34624     
34625     
34626     borderWidth: Roo.isBorderBox ? 0 : 2, 
34627     headEls : false,
34628     
34629     render : function(){
34630         // add the header.....
34631        
34632         Roo.tree.ColumnTree.superclass.render.apply(this);
34633         
34634         this.el.addClass('x-column-tree');
34635         
34636         this.headers = this.el.createChild(
34637             {cls:'x-tree-headers'},this.innerCt.dom);
34638    
34639         var cols = this.columns, c;
34640         var totalWidth = 0;
34641         this.headEls = [];
34642         var  len = cols.length;
34643         for(var i = 0; i < len; i++){
34644              c = cols[i];
34645              totalWidth += c.width;
34646             this.headEls.push(this.headers.createChild({
34647                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
34648                  cn: {
34649                      cls:'x-tree-hd-text',
34650                      html: c.header
34651                  },
34652                  style:'width:'+(c.width-this.borderWidth)+'px;'
34653              }));
34654         }
34655         this.headers.createChild({cls:'x-clear'});
34656         // prevent floats from wrapping when clipped
34657         this.headers.setWidth(totalWidth);
34658         //this.innerCt.setWidth(totalWidth);
34659         this.innerCt.setStyle({ overflow: 'auto' });
34660         this.onResize(this.width, this.height);
34661              
34662         
34663     },
34664     onResize : function(w,h)
34665     {
34666         this.height = h;
34667         this.width = w;
34668         // resize cols..
34669         this.innerCt.setWidth(this.width);
34670         this.innerCt.setHeight(this.height-20);
34671         
34672         // headers...
34673         var cols = this.columns, c;
34674         var totalWidth = 0;
34675         var expEl = false;
34676         var len = cols.length;
34677         for(var i = 0; i < len; i++){
34678             c = cols[i];
34679             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34680                 // it's the expander..
34681                 expEl  = this.headEls[i];
34682                 continue;
34683             }
34684             totalWidth += c.width;
34685             
34686         }
34687         if (expEl) {
34688             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34689         }
34690         this.headers.setWidth(w-20);
34691
34692         
34693         
34694         
34695     }
34696 });
34697 /*
34698  * Based on:
34699  * Ext JS Library 1.1.1
34700  * Copyright(c) 2006-2007, Ext JS, LLC.
34701  *
34702  * Originally Released Under LGPL - original licence link has changed is not relivant.
34703  *
34704  * Fork - LGPL
34705  * <script type="text/javascript">
34706  */
34707  
34708 /**
34709  * @class Roo.menu.Menu
34710  * @extends Roo.util.Observable
34711  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34712  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34713  * @constructor
34714  * Creates a new Menu
34715  * @param {Object} config Configuration options
34716  */
34717 Roo.menu.Menu = function(config){
34718     Roo.apply(this, config);
34719     this.id = this.id || Roo.id();
34720     this.addEvents({
34721         /**
34722          * @event beforeshow
34723          * Fires before this menu is displayed
34724          * @param {Roo.menu.Menu} this
34725          */
34726         beforeshow : true,
34727         /**
34728          * @event beforehide
34729          * Fires before this menu is hidden
34730          * @param {Roo.menu.Menu} this
34731          */
34732         beforehide : true,
34733         /**
34734          * @event show
34735          * Fires after this menu is displayed
34736          * @param {Roo.menu.Menu} this
34737          */
34738         show : true,
34739         /**
34740          * @event hide
34741          * Fires after this menu is hidden
34742          * @param {Roo.menu.Menu} this
34743          */
34744         hide : true,
34745         /**
34746          * @event click
34747          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34748          * @param {Roo.menu.Menu} this
34749          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34750          * @param {Roo.EventObject} e
34751          */
34752         click : true,
34753         /**
34754          * @event mouseover
34755          * Fires when the mouse is hovering over this menu
34756          * @param {Roo.menu.Menu} this
34757          * @param {Roo.EventObject} e
34758          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34759          */
34760         mouseover : true,
34761         /**
34762          * @event mouseout
34763          * Fires when the mouse exits this menu
34764          * @param {Roo.menu.Menu} this
34765          * @param {Roo.EventObject} e
34766          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34767          */
34768         mouseout : true,
34769         /**
34770          * @event itemclick
34771          * Fires when a menu item contained in this menu is clicked
34772          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34773          * @param {Roo.EventObject} e
34774          */
34775         itemclick: true
34776     });
34777     if (this.registerMenu) {
34778         Roo.menu.MenuMgr.register(this);
34779     }
34780     
34781     var mis = this.items;
34782     this.items = new Roo.util.MixedCollection();
34783     if(mis){
34784         this.add.apply(this, mis);
34785     }
34786 };
34787
34788 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34789     /**
34790      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34791      */
34792     minWidth : 120,
34793     /**
34794      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34795      * for bottom-right shadow (defaults to "sides")
34796      */
34797     shadow : "sides",
34798     /**
34799      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34800      * this menu (defaults to "tl-tr?")
34801      */
34802     subMenuAlign : "tl-tr?",
34803     /**
34804      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34805      * relative to its element of origin (defaults to "tl-bl?")
34806      */
34807     defaultAlign : "tl-bl?",
34808     /**
34809      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34810      */
34811     allowOtherMenus : false,
34812     /**
34813      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34814      */
34815     registerMenu : true,
34816
34817     hidden:true,
34818
34819     // private
34820     render : function(){
34821         if(this.el){
34822             return;
34823         }
34824         var el = this.el = new Roo.Layer({
34825             cls: "x-menu",
34826             shadow:this.shadow,
34827             constrain: false,
34828             parentEl: this.parentEl || document.body,
34829             zindex:15000
34830         });
34831
34832         this.keyNav = new Roo.menu.MenuNav(this);
34833
34834         if(this.plain){
34835             el.addClass("x-menu-plain");
34836         }
34837         if(this.cls){
34838             el.addClass(this.cls);
34839         }
34840         // generic focus element
34841         this.focusEl = el.createChild({
34842             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34843         });
34844         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34845         ul.on("click", this.onClick, this);
34846         ul.on("mouseover", this.onMouseOver, this);
34847         ul.on("mouseout", this.onMouseOut, this);
34848         this.items.each(function(item){
34849             if (item.hidden) {
34850                 return;
34851             }
34852             
34853             var li = document.createElement("li");
34854             li.className = "x-menu-list-item";
34855             ul.dom.appendChild(li);
34856             item.render(li, this);
34857         }, this);
34858         this.ul = ul;
34859         this.autoWidth();
34860     },
34861
34862     // private
34863     autoWidth : function(){
34864         var el = this.el, ul = this.ul;
34865         if(!el){
34866             return;
34867         }
34868         var w = this.width;
34869         if(w){
34870             el.setWidth(w);
34871         }else if(Roo.isIE){
34872             el.setWidth(this.minWidth);
34873             var t = el.dom.offsetWidth; // force recalc
34874             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34875         }
34876     },
34877
34878     // private
34879     delayAutoWidth : function(){
34880         if(this.rendered){
34881             if(!this.awTask){
34882                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34883             }
34884             this.awTask.delay(20);
34885         }
34886     },
34887
34888     // private
34889     findTargetItem : function(e){
34890         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34891         if(t && t.menuItemId){
34892             return this.items.get(t.menuItemId);
34893         }
34894     },
34895
34896     // private
34897     onClick : function(e){
34898         var t;
34899         if(t = this.findTargetItem(e)){
34900             t.onClick(e);
34901             this.fireEvent("click", this, t, e);
34902         }
34903     },
34904
34905     // private
34906     setActiveItem : function(item, autoExpand){
34907         if(item != this.activeItem){
34908             if(this.activeItem){
34909                 this.activeItem.deactivate();
34910             }
34911             this.activeItem = item;
34912             item.activate(autoExpand);
34913         }else if(autoExpand){
34914             item.expandMenu();
34915         }
34916     },
34917
34918     // private
34919     tryActivate : function(start, step){
34920         var items = this.items;
34921         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34922             var item = items.get(i);
34923             if(!item.disabled && item.canActivate){
34924                 this.setActiveItem(item, false);
34925                 return item;
34926             }
34927         }
34928         return false;
34929     },
34930
34931     // private
34932     onMouseOver : function(e){
34933         var t;
34934         if(t = this.findTargetItem(e)){
34935             if(t.canActivate && !t.disabled){
34936                 this.setActiveItem(t, true);
34937             }
34938         }
34939         this.fireEvent("mouseover", this, e, t);
34940     },
34941
34942     // private
34943     onMouseOut : function(e){
34944         var t;
34945         if(t = this.findTargetItem(e)){
34946             if(t == this.activeItem && t.shouldDeactivate(e)){
34947                 this.activeItem.deactivate();
34948                 delete this.activeItem;
34949             }
34950         }
34951         this.fireEvent("mouseout", this, e, t);
34952     },
34953
34954     /**
34955      * Read-only.  Returns true if the menu is currently displayed, else false.
34956      * @type Boolean
34957      */
34958     isVisible : function(){
34959         return this.el && !this.hidden;
34960     },
34961
34962     /**
34963      * Displays this menu relative to another element
34964      * @param {String/HTMLElement/Roo.Element} element The element to align to
34965      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34966      * the element (defaults to this.defaultAlign)
34967      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34968      */
34969     show : function(el, pos, parentMenu){
34970         this.parentMenu = parentMenu;
34971         if(!this.el){
34972             this.render();
34973         }
34974         this.fireEvent("beforeshow", this);
34975         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34976     },
34977
34978     /**
34979      * Displays this menu at a specific xy position
34980      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34981      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34982      */
34983     showAt : function(xy, parentMenu, /* private: */_e){
34984         this.parentMenu = parentMenu;
34985         if(!this.el){
34986             this.render();
34987         }
34988         if(_e !== false){
34989             this.fireEvent("beforeshow", this);
34990             xy = this.el.adjustForConstraints(xy);
34991         }
34992         this.el.setXY(xy);
34993         this.el.show();
34994         this.hidden = false;
34995         this.focus();
34996         this.fireEvent("show", this);
34997     },
34998
34999     focus : function(){
35000         if(!this.hidden){
35001             this.doFocus.defer(50, this);
35002         }
35003     },
35004
35005     doFocus : function(){
35006         if(!this.hidden){
35007             this.focusEl.focus();
35008         }
35009     },
35010
35011     /**
35012      * Hides this menu and optionally all parent menus
35013      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35014      */
35015     hide : function(deep){
35016         if(this.el && this.isVisible()){
35017             this.fireEvent("beforehide", this);
35018             if(this.activeItem){
35019                 this.activeItem.deactivate();
35020                 this.activeItem = null;
35021             }
35022             this.el.hide();
35023             this.hidden = true;
35024             this.fireEvent("hide", this);
35025         }
35026         if(deep === true && this.parentMenu){
35027             this.parentMenu.hide(true);
35028         }
35029     },
35030
35031     /**
35032      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35033      * Any of the following are valid:
35034      * <ul>
35035      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35036      * <li>An HTMLElement object which will be converted to a menu item</li>
35037      * <li>A menu item config object that will be created as a new menu item</li>
35038      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35039      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35040      * </ul>
35041      * Usage:
35042      * <pre><code>
35043 // Create the menu
35044 var menu = new Roo.menu.Menu();
35045
35046 // Create a menu item to add by reference
35047 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35048
35049 // Add a bunch of items at once using different methods.
35050 // Only the last item added will be returned.
35051 var item = menu.add(
35052     menuItem,                // add existing item by ref
35053     'Dynamic Item',          // new TextItem
35054     '-',                     // new separator
35055     { text: 'Config Item' }  // new item by config
35056 );
35057 </code></pre>
35058      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35059      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35060      */
35061     add : function(){
35062         var a = arguments, l = a.length, item;
35063         for(var i = 0; i < l; i++){
35064             var el = a[i];
35065             if ((typeof(el) == "object") && el.xtype && el.xns) {
35066                 el = Roo.factory(el, Roo.menu);
35067             }
35068             
35069             if(el.render){ // some kind of Item
35070                 item = this.addItem(el);
35071             }else if(typeof el == "string"){ // string
35072                 if(el == "separator" || el == "-"){
35073                     item = this.addSeparator();
35074                 }else{
35075                     item = this.addText(el);
35076                 }
35077             }else if(el.tagName || el.el){ // element
35078                 item = this.addElement(el);
35079             }else if(typeof el == "object"){ // must be menu item config?
35080                 item = this.addMenuItem(el);
35081             }
35082         }
35083         return item;
35084     },
35085
35086     /**
35087      * Returns this menu's underlying {@link Roo.Element} object
35088      * @return {Roo.Element} The element
35089      */
35090     getEl : function(){
35091         if(!this.el){
35092             this.render();
35093         }
35094         return this.el;
35095     },
35096
35097     /**
35098      * Adds a separator bar to the menu
35099      * @return {Roo.menu.Item} The menu item that was added
35100      */
35101     addSeparator : function(){
35102         return this.addItem(new Roo.menu.Separator());
35103     },
35104
35105     /**
35106      * Adds an {@link Roo.Element} object to the menu
35107      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35108      * @return {Roo.menu.Item} The menu item that was added
35109      */
35110     addElement : function(el){
35111         return this.addItem(new Roo.menu.BaseItem(el));
35112     },
35113
35114     /**
35115      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35116      * @param {Roo.menu.Item} item The menu item to add
35117      * @return {Roo.menu.Item} The menu item that was added
35118      */
35119     addItem : function(item){
35120         this.items.add(item);
35121         if(this.ul){
35122             var li = document.createElement("li");
35123             li.className = "x-menu-list-item";
35124             this.ul.dom.appendChild(li);
35125             item.render(li, this);
35126             this.delayAutoWidth();
35127         }
35128         return item;
35129     },
35130
35131     /**
35132      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35133      * @param {Object} config A MenuItem config object
35134      * @return {Roo.menu.Item} The menu item that was added
35135      */
35136     addMenuItem : function(config){
35137         if(!(config instanceof Roo.menu.Item)){
35138             if(typeof config.checked == "boolean"){ // must be check menu item config?
35139                 config = new Roo.menu.CheckItem(config);
35140             }else{
35141                 config = new Roo.menu.Item(config);
35142             }
35143         }
35144         return this.addItem(config);
35145     },
35146
35147     /**
35148      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35149      * @param {String} text The text to display in the menu item
35150      * @return {Roo.menu.Item} The menu item that was added
35151      */
35152     addText : function(text){
35153         return this.addItem(new Roo.menu.TextItem({ text : text }));
35154     },
35155
35156     /**
35157      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35158      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35159      * @param {Roo.menu.Item} item The menu item to add
35160      * @return {Roo.menu.Item} The menu item that was added
35161      */
35162     insert : function(index, item){
35163         this.items.insert(index, item);
35164         if(this.ul){
35165             var li = document.createElement("li");
35166             li.className = "x-menu-list-item";
35167             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35168             item.render(li, this);
35169             this.delayAutoWidth();
35170         }
35171         return item;
35172     },
35173
35174     /**
35175      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35176      * @param {Roo.menu.Item} item The menu item to remove
35177      */
35178     remove : function(item){
35179         this.items.removeKey(item.id);
35180         item.destroy();
35181     },
35182
35183     /**
35184      * Removes and destroys all items in the menu
35185      */
35186     removeAll : function(){
35187         var f;
35188         while(f = this.items.first()){
35189             this.remove(f);
35190         }
35191     }
35192 });
35193
35194 // MenuNav is a private utility class used internally by the Menu
35195 Roo.menu.MenuNav = function(menu){
35196     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35197     this.scope = this.menu = menu;
35198 };
35199
35200 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35201     doRelay : function(e, h){
35202         var k = e.getKey();
35203         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35204             this.menu.tryActivate(0, 1);
35205             return false;
35206         }
35207         return h.call(this.scope || this, e, this.menu);
35208     },
35209
35210     up : function(e, m){
35211         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35212             m.tryActivate(m.items.length-1, -1);
35213         }
35214     },
35215
35216     down : function(e, m){
35217         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35218             m.tryActivate(0, 1);
35219         }
35220     },
35221
35222     right : function(e, m){
35223         if(m.activeItem){
35224             m.activeItem.expandMenu(true);
35225         }
35226     },
35227
35228     left : function(e, m){
35229         m.hide();
35230         if(m.parentMenu && m.parentMenu.activeItem){
35231             m.parentMenu.activeItem.activate();
35232         }
35233     },
35234
35235     enter : function(e, m){
35236         if(m.activeItem){
35237             e.stopPropagation();
35238             m.activeItem.onClick(e);
35239             m.fireEvent("click", this, m.activeItem);
35240             return true;
35241         }
35242     }
35243 });/*
35244  * Based on:
35245  * Ext JS Library 1.1.1
35246  * Copyright(c) 2006-2007, Ext JS, LLC.
35247  *
35248  * Originally Released Under LGPL - original licence link has changed is not relivant.
35249  *
35250  * Fork - LGPL
35251  * <script type="text/javascript">
35252  */
35253  
35254 /**
35255  * @class Roo.menu.MenuMgr
35256  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35257  * @singleton
35258  */
35259 Roo.menu.MenuMgr = function(){
35260    var menus, active, groups = {}, attached = false, lastShow = new Date();
35261
35262    // private - called when first menu is created
35263    function init(){
35264        menus = {};
35265        active = new Roo.util.MixedCollection();
35266        Roo.get(document).addKeyListener(27, function(){
35267            if(active.length > 0){
35268                hideAll();
35269            }
35270        });
35271    }
35272
35273    // private
35274    function hideAll(){
35275        if(active && active.length > 0){
35276            var c = active.clone();
35277            c.each(function(m){
35278                m.hide();
35279            });
35280        }
35281    }
35282
35283    // private
35284    function onHide(m){
35285        active.remove(m);
35286        if(active.length < 1){
35287            Roo.get(document).un("mousedown", onMouseDown);
35288            attached = false;
35289        }
35290    }
35291
35292    // private
35293    function onShow(m){
35294        var last = active.last();
35295        lastShow = new Date();
35296        active.add(m);
35297        if(!attached){
35298            Roo.get(document).on("mousedown", onMouseDown);
35299            attached = true;
35300        }
35301        if(m.parentMenu){
35302           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35303           m.parentMenu.activeChild = m;
35304        }else if(last && last.isVisible()){
35305           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35306        }
35307    }
35308
35309    // private
35310    function onBeforeHide(m){
35311        if(m.activeChild){
35312            m.activeChild.hide();
35313        }
35314        if(m.autoHideTimer){
35315            clearTimeout(m.autoHideTimer);
35316            delete m.autoHideTimer;
35317        }
35318    }
35319
35320    // private
35321    function onBeforeShow(m){
35322        var pm = m.parentMenu;
35323        if(!pm && !m.allowOtherMenus){
35324            hideAll();
35325        }else if(pm && pm.activeChild && active != m){
35326            pm.activeChild.hide();
35327        }
35328    }
35329
35330    // private
35331    function onMouseDown(e){
35332        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35333            hideAll();
35334        }
35335    }
35336
35337    // private
35338    function onBeforeCheck(mi, state){
35339        if(state){
35340            var g = groups[mi.group];
35341            for(var i = 0, l = g.length; i < l; i++){
35342                if(g[i] != mi){
35343                    g[i].setChecked(false);
35344                }
35345            }
35346        }
35347    }
35348
35349    return {
35350
35351        /**
35352         * Hides all menus that are currently visible
35353         */
35354        hideAll : function(){
35355             hideAll();  
35356        },
35357
35358        // private
35359        register : function(menu){
35360            if(!menus){
35361                init();
35362            }
35363            menus[menu.id] = menu;
35364            menu.on("beforehide", onBeforeHide);
35365            menu.on("hide", onHide);
35366            menu.on("beforeshow", onBeforeShow);
35367            menu.on("show", onShow);
35368            var g = menu.group;
35369            if(g && menu.events["checkchange"]){
35370                if(!groups[g]){
35371                    groups[g] = [];
35372                }
35373                groups[g].push(menu);
35374                menu.on("checkchange", onCheck);
35375            }
35376        },
35377
35378         /**
35379          * Returns a {@link Roo.menu.Menu} object
35380          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
35381          * be used to generate and return a new Menu instance.
35382          */
35383        get : function(menu){
35384            if(typeof menu == "string"){ // menu id
35385                return menus[menu];
35386            }else if(menu.events){  // menu instance
35387                return menu;
35388            }else if(typeof menu.length == 'number'){ // array of menu items?
35389                return new Roo.menu.Menu({items:menu});
35390            }else{ // otherwise, must be a config
35391                return new Roo.menu.Menu(menu);
35392            }
35393        },
35394
35395        // private
35396        unregister : function(menu){
35397            delete menus[menu.id];
35398            menu.un("beforehide", onBeforeHide);
35399            menu.un("hide", onHide);
35400            menu.un("beforeshow", onBeforeShow);
35401            menu.un("show", onShow);
35402            var g = menu.group;
35403            if(g && menu.events["checkchange"]){
35404                groups[g].remove(menu);
35405                menu.un("checkchange", onCheck);
35406            }
35407        },
35408
35409        // private
35410        registerCheckable : function(menuItem){
35411            var g = menuItem.group;
35412            if(g){
35413                if(!groups[g]){
35414                    groups[g] = [];
35415                }
35416                groups[g].push(menuItem);
35417                menuItem.on("beforecheckchange", onBeforeCheck);
35418            }
35419        },
35420
35421        // private
35422        unregisterCheckable : function(menuItem){
35423            var g = menuItem.group;
35424            if(g){
35425                groups[g].remove(menuItem);
35426                menuItem.un("beforecheckchange", onBeforeCheck);
35427            }
35428        }
35429    };
35430 }();/*
35431  * Based on:
35432  * Ext JS Library 1.1.1
35433  * Copyright(c) 2006-2007, Ext JS, LLC.
35434  *
35435  * Originally Released Under LGPL - original licence link has changed is not relivant.
35436  *
35437  * Fork - LGPL
35438  * <script type="text/javascript">
35439  */
35440  
35441
35442 /**
35443  * @class Roo.menu.BaseItem
35444  * @extends Roo.Component
35445  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
35446  * management and base configuration options shared by all menu components.
35447  * @constructor
35448  * Creates a new BaseItem
35449  * @param {Object} config Configuration options
35450  */
35451 Roo.menu.BaseItem = function(config){
35452     Roo.menu.BaseItem.superclass.constructor.call(this, config);
35453
35454     this.addEvents({
35455         /**
35456          * @event click
35457          * Fires when this item is clicked
35458          * @param {Roo.menu.BaseItem} this
35459          * @param {Roo.EventObject} e
35460          */
35461         click: true,
35462         /**
35463          * @event activate
35464          * Fires when this item is activated
35465          * @param {Roo.menu.BaseItem} this
35466          */
35467         activate : true,
35468         /**
35469          * @event deactivate
35470          * Fires when this item is deactivated
35471          * @param {Roo.menu.BaseItem} this
35472          */
35473         deactivate : true
35474     });
35475
35476     if(this.handler){
35477         this.on("click", this.handler, this.scope, true);
35478     }
35479 };
35480
35481 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
35482     /**
35483      * @cfg {Function} handler
35484      * A function that will handle the click event of this menu item (defaults to undefined)
35485      */
35486     /**
35487      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
35488      */
35489     canActivate : false,
35490     
35491      /**
35492      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
35493      */
35494     hidden: false,
35495     
35496     /**
35497      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
35498      */
35499     activeClass : "x-menu-item-active",
35500     /**
35501      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
35502      */
35503     hideOnClick : true,
35504     /**
35505      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
35506      */
35507     hideDelay : 100,
35508
35509     // private
35510     ctype: "Roo.menu.BaseItem",
35511
35512     // private
35513     actionMode : "container",
35514
35515     // private
35516     render : function(container, parentMenu){
35517         this.parentMenu = parentMenu;
35518         Roo.menu.BaseItem.superclass.render.call(this, container);
35519         this.container.menuItemId = this.id;
35520     },
35521
35522     // private
35523     onRender : function(container, position){
35524         this.el = Roo.get(this.el);
35525         container.dom.appendChild(this.el.dom);
35526     },
35527
35528     // private
35529     onClick : function(e){
35530         if(!this.disabled && this.fireEvent("click", this, e) !== false
35531                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
35532             this.handleClick(e);
35533         }else{
35534             e.stopEvent();
35535         }
35536     },
35537
35538     // private
35539     activate : function(){
35540         if(this.disabled){
35541             return false;
35542         }
35543         var li = this.container;
35544         li.addClass(this.activeClass);
35545         this.region = li.getRegion().adjust(2, 2, -2, -2);
35546         this.fireEvent("activate", this);
35547         return true;
35548     },
35549
35550     // private
35551     deactivate : function(){
35552         this.container.removeClass(this.activeClass);
35553         this.fireEvent("deactivate", this);
35554     },
35555
35556     // private
35557     shouldDeactivate : function(e){
35558         return !this.region || !this.region.contains(e.getPoint());
35559     },
35560
35561     // private
35562     handleClick : function(e){
35563         if(this.hideOnClick){
35564             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
35565         }
35566     },
35567
35568     // private
35569     expandMenu : function(autoActivate){
35570         // do nothing
35571     },
35572
35573     // private
35574     hideMenu : function(){
35575         // do nothing
35576     }
35577 });/*
35578  * Based on:
35579  * Ext JS Library 1.1.1
35580  * Copyright(c) 2006-2007, Ext JS, LLC.
35581  *
35582  * Originally Released Under LGPL - original licence link has changed is not relivant.
35583  *
35584  * Fork - LGPL
35585  * <script type="text/javascript">
35586  */
35587  
35588 /**
35589  * @class Roo.menu.Adapter
35590  * @extends Roo.menu.BaseItem
35591  * 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.
35592  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
35593  * @constructor
35594  * Creates a new Adapter
35595  * @param {Object} config Configuration options
35596  */
35597 Roo.menu.Adapter = function(component, config){
35598     Roo.menu.Adapter.superclass.constructor.call(this, config);
35599     this.component = component;
35600 };
35601 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
35602     // private
35603     canActivate : true,
35604
35605     // private
35606     onRender : function(container, position){
35607         this.component.render(container);
35608         this.el = this.component.getEl();
35609     },
35610
35611     // private
35612     activate : function(){
35613         if(this.disabled){
35614             return false;
35615         }
35616         this.component.focus();
35617         this.fireEvent("activate", this);
35618         return true;
35619     },
35620
35621     // private
35622     deactivate : function(){
35623         this.fireEvent("deactivate", this);
35624     },
35625
35626     // private
35627     disable : function(){
35628         this.component.disable();
35629         Roo.menu.Adapter.superclass.disable.call(this);
35630     },
35631
35632     // private
35633     enable : function(){
35634         this.component.enable();
35635         Roo.menu.Adapter.superclass.enable.call(this);
35636     }
35637 });/*
35638  * Based on:
35639  * Ext JS Library 1.1.1
35640  * Copyright(c) 2006-2007, Ext JS, LLC.
35641  *
35642  * Originally Released Under LGPL - original licence link has changed is not relivant.
35643  *
35644  * Fork - LGPL
35645  * <script type="text/javascript">
35646  */
35647
35648 /**
35649  * @class Roo.menu.TextItem
35650  * @extends Roo.menu.BaseItem
35651  * Adds a static text string to a menu, usually used as either a heading or group separator.
35652  * Note: old style constructor with text is still supported.
35653  * 
35654  * @constructor
35655  * Creates a new TextItem
35656  * @param {Object} cfg Configuration
35657  */
35658 Roo.menu.TextItem = function(cfg){
35659     if (typeof(cfg) == 'string') {
35660         this.text = cfg;
35661     } else {
35662         Roo.apply(this,cfg);
35663     }
35664     
35665     Roo.menu.TextItem.superclass.constructor.call(this);
35666 };
35667
35668 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
35669     /**
35670      * @cfg {Boolean} text Text to show on item.
35671      */
35672     text : '',
35673     
35674     /**
35675      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35676      */
35677     hideOnClick : false,
35678     /**
35679      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35680      */
35681     itemCls : "x-menu-text",
35682
35683     // private
35684     onRender : function(){
35685         var s = document.createElement("span");
35686         s.className = this.itemCls;
35687         s.innerHTML = this.text;
35688         this.el = s;
35689         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35690     }
35691 });/*
35692  * Based on:
35693  * Ext JS Library 1.1.1
35694  * Copyright(c) 2006-2007, Ext JS, LLC.
35695  *
35696  * Originally Released Under LGPL - original licence link has changed is not relivant.
35697  *
35698  * Fork - LGPL
35699  * <script type="text/javascript">
35700  */
35701
35702 /**
35703  * @class Roo.menu.Separator
35704  * @extends Roo.menu.BaseItem
35705  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35706  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35707  * @constructor
35708  * @param {Object} config Configuration options
35709  */
35710 Roo.menu.Separator = function(config){
35711     Roo.menu.Separator.superclass.constructor.call(this, config);
35712 };
35713
35714 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35715     /**
35716      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35717      */
35718     itemCls : "x-menu-sep",
35719     /**
35720      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35721      */
35722     hideOnClick : false,
35723
35724     // private
35725     onRender : function(li){
35726         var s = document.createElement("span");
35727         s.className = this.itemCls;
35728         s.innerHTML = "&#160;";
35729         this.el = s;
35730         li.addClass("x-menu-sep-li");
35731         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35732     }
35733 });/*
35734  * Based on:
35735  * Ext JS Library 1.1.1
35736  * Copyright(c) 2006-2007, Ext JS, LLC.
35737  *
35738  * Originally Released Under LGPL - original licence link has changed is not relivant.
35739  *
35740  * Fork - LGPL
35741  * <script type="text/javascript">
35742  */
35743 /**
35744  * @class Roo.menu.Item
35745  * @extends Roo.menu.BaseItem
35746  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35747  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35748  * activation and click handling.
35749  * @constructor
35750  * Creates a new Item
35751  * @param {Object} config Configuration options
35752  */
35753 Roo.menu.Item = function(config){
35754     Roo.menu.Item.superclass.constructor.call(this, config);
35755     if(this.menu){
35756         this.menu = Roo.menu.MenuMgr.get(this.menu);
35757     }
35758 };
35759 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35760     
35761     /**
35762      * @cfg {String} text
35763      * The text to show on the menu item.
35764      */
35765     text: '',
35766      /**
35767      * @cfg {String} HTML to render in menu
35768      * The text to show on the menu item (HTML version).
35769      */
35770     html: '',
35771     /**
35772      * @cfg {String} icon
35773      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35774      */
35775     icon: undefined,
35776     /**
35777      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35778      */
35779     itemCls : "x-menu-item",
35780     /**
35781      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35782      */
35783     canActivate : true,
35784     /**
35785      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35786      */
35787     showDelay: 200,
35788     // doc'd in BaseItem
35789     hideDelay: 200,
35790
35791     // private
35792     ctype: "Roo.menu.Item",
35793     
35794     // private
35795     onRender : function(container, position){
35796         var el = document.createElement("a");
35797         el.hideFocus = true;
35798         el.unselectable = "on";
35799         el.href = this.href || "#";
35800         if(this.hrefTarget){
35801             el.target = this.hrefTarget;
35802         }
35803         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35804         
35805         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35806         
35807         el.innerHTML = String.format(
35808                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35809                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35810         this.el = el;
35811         Roo.menu.Item.superclass.onRender.call(this, container, position);
35812     },
35813
35814     /**
35815      * Sets the text to display in this menu item
35816      * @param {String} text The text to display
35817      * @param {Boolean} isHTML true to indicate text is pure html.
35818      */
35819     setText : function(text, isHTML){
35820         if (isHTML) {
35821             this.html = text;
35822         } else {
35823             this.text = text;
35824             this.html = '';
35825         }
35826         if(this.rendered){
35827             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35828      
35829             this.el.update(String.format(
35830                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35831                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35832             this.parentMenu.autoWidth();
35833         }
35834     },
35835
35836     // private
35837     handleClick : function(e){
35838         if(!this.href){ // if no link defined, stop the event automatically
35839             e.stopEvent();
35840         }
35841         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35842     },
35843
35844     // private
35845     activate : function(autoExpand){
35846         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35847             this.focus();
35848             if(autoExpand){
35849                 this.expandMenu();
35850             }
35851         }
35852         return true;
35853     },
35854
35855     // private
35856     shouldDeactivate : function(e){
35857         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35858             if(this.menu && this.menu.isVisible()){
35859                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35860             }
35861             return true;
35862         }
35863         return false;
35864     },
35865
35866     // private
35867     deactivate : function(){
35868         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35869         this.hideMenu();
35870     },
35871
35872     // private
35873     expandMenu : function(autoActivate){
35874         if(!this.disabled && this.menu){
35875             clearTimeout(this.hideTimer);
35876             delete this.hideTimer;
35877             if(!this.menu.isVisible() && !this.showTimer){
35878                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35879             }else if (this.menu.isVisible() && autoActivate){
35880                 this.menu.tryActivate(0, 1);
35881             }
35882         }
35883     },
35884
35885     // private
35886     deferExpand : function(autoActivate){
35887         delete this.showTimer;
35888         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35889         if(autoActivate){
35890             this.menu.tryActivate(0, 1);
35891         }
35892     },
35893
35894     // private
35895     hideMenu : function(){
35896         clearTimeout(this.showTimer);
35897         delete this.showTimer;
35898         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35899             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35900         }
35901     },
35902
35903     // private
35904     deferHide : function(){
35905         delete this.hideTimer;
35906         this.menu.hide();
35907     }
35908 });/*
35909  * Based on:
35910  * Ext JS Library 1.1.1
35911  * Copyright(c) 2006-2007, Ext JS, LLC.
35912  *
35913  * Originally Released Under LGPL - original licence link has changed is not relivant.
35914  *
35915  * Fork - LGPL
35916  * <script type="text/javascript">
35917  */
35918  
35919 /**
35920  * @class Roo.menu.CheckItem
35921  * @extends Roo.menu.Item
35922  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35923  * @constructor
35924  * Creates a new CheckItem
35925  * @param {Object} config Configuration options
35926  */
35927 Roo.menu.CheckItem = function(config){
35928     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35929     this.addEvents({
35930         /**
35931          * @event beforecheckchange
35932          * Fires before the checked value is set, providing an opportunity to cancel if needed
35933          * @param {Roo.menu.CheckItem} this
35934          * @param {Boolean} checked The new checked value that will be set
35935          */
35936         "beforecheckchange" : true,
35937         /**
35938          * @event checkchange
35939          * Fires after the checked value has been set
35940          * @param {Roo.menu.CheckItem} this
35941          * @param {Boolean} checked The checked value that was set
35942          */
35943         "checkchange" : true
35944     });
35945     if(this.checkHandler){
35946         this.on('checkchange', this.checkHandler, this.scope);
35947     }
35948 };
35949 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35950     /**
35951      * @cfg {String} group
35952      * All check items with the same group name will automatically be grouped into a single-select
35953      * radio button group (defaults to '')
35954      */
35955     /**
35956      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35957      */
35958     itemCls : "x-menu-item x-menu-check-item",
35959     /**
35960      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35961      */
35962     groupClass : "x-menu-group-item",
35963
35964     /**
35965      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35966      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35967      * initialized with checked = true will be rendered as checked.
35968      */
35969     checked: false,
35970
35971     // private
35972     ctype: "Roo.menu.CheckItem",
35973
35974     // private
35975     onRender : function(c){
35976         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35977         if(this.group){
35978             this.el.addClass(this.groupClass);
35979         }
35980         Roo.menu.MenuMgr.registerCheckable(this);
35981         if(this.checked){
35982             this.checked = false;
35983             this.setChecked(true, true);
35984         }
35985     },
35986
35987     // private
35988     destroy : function(){
35989         if(this.rendered){
35990             Roo.menu.MenuMgr.unregisterCheckable(this);
35991         }
35992         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35993     },
35994
35995     /**
35996      * Set the checked state of this item
35997      * @param {Boolean} checked The new checked value
35998      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35999      */
36000     setChecked : function(state, suppressEvent){
36001         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36002             if(this.container){
36003                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36004             }
36005             this.checked = state;
36006             if(suppressEvent !== true){
36007                 this.fireEvent("checkchange", this, state);
36008             }
36009         }
36010     },
36011
36012     // private
36013     handleClick : function(e){
36014        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36015            this.setChecked(!this.checked);
36016        }
36017        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36018     }
36019 });/*
36020  * Based on:
36021  * Ext JS Library 1.1.1
36022  * Copyright(c) 2006-2007, Ext JS, LLC.
36023  *
36024  * Originally Released Under LGPL - original licence link has changed is not relivant.
36025  *
36026  * Fork - LGPL
36027  * <script type="text/javascript">
36028  */
36029  
36030 /**
36031  * @class Roo.menu.DateItem
36032  * @extends Roo.menu.Adapter
36033  * A menu item that wraps the {@link Roo.DatPicker} component.
36034  * @constructor
36035  * Creates a new DateItem
36036  * @param {Object} config Configuration options
36037  */
36038 Roo.menu.DateItem = function(config){
36039     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36040     /** The Roo.DatePicker object @type Roo.DatePicker */
36041     this.picker = this.component;
36042     this.addEvents({select: true});
36043     
36044     this.picker.on("render", function(picker){
36045         picker.getEl().swallowEvent("click");
36046         picker.container.addClass("x-menu-date-item");
36047     });
36048
36049     this.picker.on("select", this.onSelect, this);
36050 };
36051
36052 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36053     // private
36054     onSelect : function(picker, date){
36055         this.fireEvent("select", this, date, picker);
36056         Roo.menu.DateItem.superclass.handleClick.call(this);
36057     }
36058 });/*
36059  * Based on:
36060  * Ext JS Library 1.1.1
36061  * Copyright(c) 2006-2007, Ext JS, LLC.
36062  *
36063  * Originally Released Under LGPL - original licence link has changed is not relivant.
36064  *
36065  * Fork - LGPL
36066  * <script type="text/javascript">
36067  */
36068  
36069 /**
36070  * @class Roo.menu.ColorItem
36071  * @extends Roo.menu.Adapter
36072  * A menu item that wraps the {@link Roo.ColorPalette} component.
36073  * @constructor
36074  * Creates a new ColorItem
36075  * @param {Object} config Configuration options
36076  */
36077 Roo.menu.ColorItem = function(config){
36078     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36079     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36080     this.palette = this.component;
36081     this.relayEvents(this.palette, ["select"]);
36082     if(this.selectHandler){
36083         this.on('select', this.selectHandler, this.scope);
36084     }
36085 };
36086 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36087  * Based on:
36088  * Ext JS Library 1.1.1
36089  * Copyright(c) 2006-2007, Ext JS, LLC.
36090  *
36091  * Originally Released Under LGPL - original licence link has changed is not relivant.
36092  *
36093  * Fork - LGPL
36094  * <script type="text/javascript">
36095  */
36096  
36097
36098 /**
36099  * @class Roo.menu.DateMenu
36100  * @extends Roo.menu.Menu
36101  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36102  * @constructor
36103  * Creates a new DateMenu
36104  * @param {Object} config Configuration options
36105  */
36106 Roo.menu.DateMenu = function(config){
36107     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36108     this.plain = true;
36109     var di = new Roo.menu.DateItem(config);
36110     this.add(di);
36111     /**
36112      * The {@link Roo.DatePicker} instance for this DateMenu
36113      * @type DatePicker
36114      */
36115     this.picker = di.picker;
36116     /**
36117      * @event select
36118      * @param {DatePicker} picker
36119      * @param {Date} date
36120      */
36121     this.relayEvents(di, ["select"]);
36122     this.on('beforeshow', function(){
36123         if(this.picker){
36124             this.picker.hideMonthPicker(false);
36125         }
36126     }, this);
36127 };
36128 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36129     cls:'x-date-menu'
36130 });/*
36131  * Based on:
36132  * Ext JS Library 1.1.1
36133  * Copyright(c) 2006-2007, Ext JS, LLC.
36134  *
36135  * Originally Released Under LGPL - original licence link has changed is not relivant.
36136  *
36137  * Fork - LGPL
36138  * <script type="text/javascript">
36139  */
36140  
36141
36142 /**
36143  * @class Roo.menu.ColorMenu
36144  * @extends Roo.menu.Menu
36145  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36146  * @constructor
36147  * Creates a new ColorMenu
36148  * @param {Object} config Configuration options
36149  */
36150 Roo.menu.ColorMenu = function(config){
36151     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36152     this.plain = true;
36153     var ci = new Roo.menu.ColorItem(config);
36154     this.add(ci);
36155     /**
36156      * The {@link Roo.ColorPalette} instance for this ColorMenu
36157      * @type ColorPalette
36158      */
36159     this.palette = ci.palette;
36160     /**
36161      * @event select
36162      * @param {ColorPalette} palette
36163      * @param {String} color
36164      */
36165     this.relayEvents(ci, ["select"]);
36166 };
36167 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36168  * Based on:
36169  * Ext JS Library 1.1.1
36170  * Copyright(c) 2006-2007, Ext JS, LLC.
36171  *
36172  * Originally Released Under LGPL - original licence link has changed is not relivant.
36173  *
36174  * Fork - LGPL
36175  * <script type="text/javascript">
36176  */
36177  
36178 /**
36179  * @class Roo.form.Field
36180  * @extends Roo.BoxComponent
36181  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36182  * @constructor
36183  * Creates a new Field
36184  * @param {Object} config Configuration options
36185  */
36186 Roo.form.Field = function(config){
36187     Roo.form.Field.superclass.constructor.call(this, config);
36188 };
36189
36190 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36191     /**
36192      * @cfg {String} fieldLabel Label to use when rendering a form.
36193      */
36194        /**
36195      * @cfg {String} qtip Mouse over tip
36196      */
36197      
36198     /**
36199      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36200      */
36201     invalidClass : "x-form-invalid",
36202     /**
36203      * @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")
36204      */
36205     invalidText : "The value in this field is invalid",
36206     /**
36207      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36208      */
36209     focusClass : "x-form-focus",
36210     /**
36211      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36212       automatic validation (defaults to "keyup").
36213      */
36214     validationEvent : "keyup",
36215     /**
36216      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36217      */
36218     validateOnBlur : true,
36219     /**
36220      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36221      */
36222     validationDelay : 250,
36223     /**
36224      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36225      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36226      */
36227     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36228     /**
36229      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36230      */
36231     fieldClass : "x-form-field",
36232     /**
36233      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36234      *<pre>
36235 Value         Description
36236 -----------   ----------------------------------------------------------------------
36237 qtip          Display a quick tip when the user hovers over the field
36238 title         Display a default browser title attribute popup
36239 under         Add a block div beneath the field containing the error text
36240 side          Add an error icon to the right of the field with a popup on hover
36241 [element id]  Add the error text directly to the innerHTML of the specified element
36242 </pre>
36243      */
36244     msgTarget : 'qtip',
36245     /**
36246      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36247      */
36248     msgFx : 'normal',
36249
36250     /**
36251      * @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.
36252      */
36253     readOnly : false,
36254
36255     /**
36256      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36257      */
36258     disabled : false,
36259
36260     /**
36261      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36262      */
36263     inputType : undefined,
36264     
36265     /**
36266      * @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).
36267          */
36268         tabIndex : undefined,
36269         
36270     // private
36271     isFormField : true,
36272
36273     // private
36274     hasFocus : false,
36275     /**
36276      * @property {Roo.Element} fieldEl
36277      * Element Containing the rendered Field (with label etc.)
36278      */
36279     /**
36280      * @cfg {Mixed} value A value to initialize this field with.
36281      */
36282     value : undefined,
36283
36284     /**
36285      * @cfg {String} name The field's HTML name attribute.
36286      */
36287     /**
36288      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36289      */
36290
36291         // private ??
36292         initComponent : function(){
36293         Roo.form.Field.superclass.initComponent.call(this);
36294         this.addEvents({
36295             /**
36296              * @event focus
36297              * Fires when this field receives input focus.
36298              * @param {Roo.form.Field} this
36299              */
36300             focus : true,
36301             /**
36302              * @event blur
36303              * Fires when this field loses input focus.
36304              * @param {Roo.form.Field} this
36305              */
36306             blur : true,
36307             /**
36308              * @event specialkey
36309              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36310              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36311              * @param {Roo.form.Field} this
36312              * @param {Roo.EventObject} e The event object
36313              */
36314             specialkey : true,
36315             /**
36316              * @event change
36317              * Fires just before the field blurs if the field value has changed.
36318              * @param {Roo.form.Field} this
36319              * @param {Mixed} newValue The new value
36320              * @param {Mixed} oldValue The original value
36321              */
36322             change : true,
36323             /**
36324              * @event invalid
36325              * Fires after the field has been marked as invalid.
36326              * @param {Roo.form.Field} this
36327              * @param {String} msg The validation message
36328              */
36329             invalid : true,
36330             /**
36331              * @event valid
36332              * Fires after the field has been validated with no errors.
36333              * @param {Roo.form.Field} this
36334              */
36335             valid : true,
36336              /**
36337              * @event keyup
36338              * Fires after the key up
36339              * @param {Roo.form.Field} this
36340              * @param {Roo.EventObject}  e The event Object
36341              */
36342             keyup : true
36343         });
36344     },
36345
36346     /**
36347      * Returns the name attribute of the field if available
36348      * @return {String} name The field name
36349      */
36350     getName: function(){
36351          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36352     },
36353
36354     // private
36355     onRender : function(ct, position){
36356         Roo.form.Field.superclass.onRender.call(this, ct, position);
36357         if(!this.el){
36358             var cfg = this.getAutoCreate();
36359             if(!cfg.name){
36360                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
36361             }
36362             if (!cfg.name.length) {
36363                 delete cfg.name;
36364             }
36365             if(this.inputType){
36366                 cfg.type = this.inputType;
36367             }
36368             this.el = ct.createChild(cfg, position);
36369         }
36370         var type = this.el.dom.type;
36371         if(type){
36372             if(type == 'password'){
36373                 type = 'text';
36374             }
36375             this.el.addClass('x-form-'+type);
36376         }
36377         if(this.readOnly){
36378             this.el.dom.readOnly = true;
36379         }
36380         if(this.tabIndex !== undefined){
36381             this.el.dom.setAttribute('tabIndex', this.tabIndex);
36382         }
36383
36384         this.el.addClass([this.fieldClass, this.cls]);
36385         this.initValue();
36386     },
36387
36388     /**
36389      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
36390      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
36391      * @return {Roo.form.Field} this
36392      */
36393     applyTo : function(target){
36394         this.allowDomMove = false;
36395         this.el = Roo.get(target);
36396         this.render(this.el.dom.parentNode);
36397         return this;
36398     },
36399
36400     // private
36401     initValue : function(){
36402         if(this.value !== undefined){
36403             this.setValue(this.value);
36404         }else if(this.el.dom.value.length > 0){
36405             this.setValue(this.el.dom.value);
36406         }
36407     },
36408
36409     /**
36410      * Returns true if this field has been changed since it was originally loaded and is not disabled.
36411      */
36412     isDirty : function() {
36413         if(this.disabled) {
36414             return false;
36415         }
36416         return String(this.getValue()) !== String(this.originalValue);
36417     },
36418
36419     // private
36420     afterRender : function(){
36421         Roo.form.Field.superclass.afterRender.call(this);
36422         this.initEvents();
36423     },
36424
36425     // private
36426     fireKey : function(e){
36427         //Roo.log('field ' + e.getKey());
36428         if(e.isNavKeyPress()){
36429             this.fireEvent("specialkey", this, e);
36430         }
36431     },
36432
36433     /**
36434      * Resets the current field value to the originally loaded value and clears any validation messages
36435      */
36436     reset : function(){
36437         this.setValue(this.originalValue);
36438         this.clearInvalid();
36439     },
36440
36441     // private
36442     initEvents : function(){
36443         // safari killled keypress - so keydown is now used..
36444         this.el.on("keydown" , this.fireKey,  this);
36445         this.el.on("focus", this.onFocus,  this);
36446         this.el.on("blur", this.onBlur,  this);
36447         this.el.relayEvent('keyup', this);
36448
36449         // reference to original value for reset
36450         this.originalValue = this.getValue();
36451     },
36452
36453     // private
36454     onFocus : function(){
36455         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36456             this.el.addClass(this.focusClass);
36457         }
36458         if(!this.hasFocus){
36459             this.hasFocus = true;
36460             this.startValue = this.getValue();
36461             this.fireEvent("focus", this);
36462         }
36463     },
36464
36465     beforeBlur : Roo.emptyFn,
36466
36467     // private
36468     onBlur : function(){
36469         this.beforeBlur();
36470         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36471             this.el.removeClass(this.focusClass);
36472         }
36473         this.hasFocus = false;
36474         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
36475             this.validate();
36476         }
36477         var v = this.getValue();
36478         if(String(v) !== String(this.startValue)){
36479             this.fireEvent('change', this, v, this.startValue);
36480         }
36481         this.fireEvent("blur", this);
36482     },
36483
36484     /**
36485      * Returns whether or not the field value is currently valid
36486      * @param {Boolean} preventMark True to disable marking the field invalid
36487      * @return {Boolean} True if the value is valid, else false
36488      */
36489     isValid : function(preventMark){
36490         if(this.disabled){
36491             return true;
36492         }
36493         var restore = this.preventMark;
36494         this.preventMark = preventMark === true;
36495         var v = this.validateValue(this.processValue(this.getRawValue()));
36496         this.preventMark = restore;
36497         return v;
36498     },
36499
36500     /**
36501      * Validates the field value
36502      * @return {Boolean} True if the value is valid, else false
36503      */
36504     validate : function(){
36505         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
36506             this.clearInvalid();
36507             return true;
36508         }
36509         return false;
36510     },
36511
36512     processValue : function(value){
36513         return value;
36514     },
36515
36516     // private
36517     // Subclasses should provide the validation implementation by overriding this
36518     validateValue : function(value){
36519         return true;
36520     },
36521
36522     /**
36523      * Mark this field as invalid
36524      * @param {String} msg The validation message
36525      */
36526     markInvalid : function(msg){
36527         if(!this.rendered || this.preventMark){ // not rendered
36528             return;
36529         }
36530         this.el.addClass(this.invalidClass);
36531         msg = msg || this.invalidText;
36532         switch(this.msgTarget){
36533             case 'qtip':
36534                 this.el.dom.qtip = msg;
36535                 this.el.dom.qclass = 'x-form-invalid-tip';
36536                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
36537                     Roo.QuickTips.enable();
36538                 }
36539                 break;
36540             case 'title':
36541                 this.el.dom.title = msg;
36542                 break;
36543             case 'under':
36544                 if(!this.errorEl){
36545                     var elp = this.el.findParent('.x-form-element', 5, true);
36546                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
36547                     this.errorEl.setWidth(elp.getWidth(true)-20);
36548                 }
36549                 this.errorEl.update(msg);
36550                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
36551                 break;
36552             case 'side':
36553                 if(!this.errorIcon){
36554                     var elp = this.el.findParent('.x-form-element', 5, true);
36555                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
36556                 }
36557                 this.alignErrorIcon();
36558                 this.errorIcon.dom.qtip = msg;
36559                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
36560                 this.errorIcon.show();
36561                 this.on('resize', this.alignErrorIcon, this);
36562                 break;
36563             default:
36564                 var t = Roo.getDom(this.msgTarget);
36565                 t.innerHTML = msg;
36566                 t.style.display = this.msgDisplay;
36567                 break;
36568         }
36569         this.fireEvent('invalid', this, msg);
36570     },
36571
36572     // private
36573     alignErrorIcon : function(){
36574         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
36575     },
36576
36577     /**
36578      * Clear any invalid styles/messages for this field
36579      */
36580     clearInvalid : function(){
36581         if(!this.rendered || this.preventMark){ // not rendered
36582             return;
36583         }
36584         this.el.removeClass(this.invalidClass);
36585         switch(this.msgTarget){
36586             case 'qtip':
36587                 this.el.dom.qtip = '';
36588                 break;
36589             case 'title':
36590                 this.el.dom.title = '';
36591                 break;
36592             case 'under':
36593                 if(this.errorEl){
36594                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
36595                 }
36596                 break;
36597             case 'side':
36598                 if(this.errorIcon){
36599                     this.errorIcon.dom.qtip = '';
36600                     this.errorIcon.hide();
36601                     this.un('resize', this.alignErrorIcon, this);
36602                 }
36603                 break;
36604             default:
36605                 var t = Roo.getDom(this.msgTarget);
36606                 t.innerHTML = '';
36607                 t.style.display = 'none';
36608                 break;
36609         }
36610         this.fireEvent('valid', this);
36611     },
36612
36613     /**
36614      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
36615      * @return {Mixed} value The field value
36616      */
36617     getRawValue : function(){
36618         var v = this.el.getValue();
36619         
36620         return v;
36621     },
36622
36623     /**
36624      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
36625      * @return {Mixed} value The field value
36626      */
36627     getValue : function(){
36628         var v = this.el.getValue();
36629          
36630         return v;
36631     },
36632
36633     /**
36634      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
36635      * @param {Mixed} value The value to set
36636      */
36637     setRawValue : function(v){
36638         return this.el.dom.value = (v === null || v === undefined ? '' : v);
36639     },
36640
36641     /**
36642      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
36643      * @param {Mixed} value The value to set
36644      */
36645     setValue : function(v){
36646         this.value = v;
36647         if(this.rendered){
36648             this.el.dom.value = (v === null || v === undefined ? '' : v);
36649              this.validate();
36650         }
36651     },
36652
36653     adjustSize : function(w, h){
36654         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
36655         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
36656         return s;
36657     },
36658
36659     adjustWidth : function(tag, w){
36660         tag = tag.toLowerCase();
36661         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
36662             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
36663                 if(tag == 'input'){
36664                     return w + 2;
36665                 }
36666                 if(tag == 'textarea'){
36667                     return w-2;
36668                 }
36669             }else if(Roo.isOpera){
36670                 if(tag == 'input'){
36671                     return w + 2;
36672                 }
36673                 if(tag == 'textarea'){
36674                     return w-2;
36675                 }
36676             }
36677         }
36678         return w;
36679     }
36680 });
36681
36682
36683 // anything other than normal should be considered experimental
36684 Roo.form.Field.msgFx = {
36685     normal : {
36686         show: function(msgEl, f){
36687             msgEl.setDisplayed('block');
36688         },
36689
36690         hide : function(msgEl, f){
36691             msgEl.setDisplayed(false).update('');
36692         }
36693     },
36694
36695     slide : {
36696         show: function(msgEl, f){
36697             msgEl.slideIn('t', {stopFx:true});
36698         },
36699
36700         hide : function(msgEl, f){
36701             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36702         }
36703     },
36704
36705     slideRight : {
36706         show: function(msgEl, f){
36707             msgEl.fixDisplay();
36708             msgEl.alignTo(f.el, 'tl-tr');
36709             msgEl.slideIn('l', {stopFx:true});
36710         },
36711
36712         hide : function(msgEl, f){
36713             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36714         }
36715     }
36716 };/*
36717  * Based on:
36718  * Ext JS Library 1.1.1
36719  * Copyright(c) 2006-2007, Ext JS, LLC.
36720  *
36721  * Originally Released Under LGPL - original licence link has changed is not relivant.
36722  *
36723  * Fork - LGPL
36724  * <script type="text/javascript">
36725  */
36726  
36727
36728 /**
36729  * @class Roo.form.TextField
36730  * @extends Roo.form.Field
36731  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36732  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36733  * @constructor
36734  * Creates a new TextField
36735  * @param {Object} config Configuration options
36736  */
36737 Roo.form.TextField = function(config){
36738     Roo.form.TextField.superclass.constructor.call(this, config);
36739     this.addEvents({
36740         /**
36741          * @event autosize
36742          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36743          * according to the default logic, but this event provides a hook for the developer to apply additional
36744          * logic at runtime to resize the field if needed.
36745              * @param {Roo.form.Field} this This text field
36746              * @param {Number} width The new field width
36747              */
36748         autosize : true
36749     });
36750 };
36751
36752 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36753     /**
36754      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36755      */
36756     grow : false,
36757     /**
36758      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36759      */
36760     growMin : 30,
36761     /**
36762      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36763      */
36764     growMax : 800,
36765     /**
36766      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36767      */
36768     vtype : null,
36769     /**
36770      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36771      */
36772     maskRe : null,
36773     /**
36774      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36775      */
36776     disableKeyFilter : false,
36777     /**
36778      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36779      */
36780     allowBlank : true,
36781     /**
36782      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36783      */
36784     minLength : 0,
36785     /**
36786      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36787      */
36788     maxLength : Number.MAX_VALUE,
36789     /**
36790      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36791      */
36792     minLengthText : "The minimum length for this field is {0}",
36793     /**
36794      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36795      */
36796     maxLengthText : "The maximum length for this field is {0}",
36797     /**
36798      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36799      */
36800     selectOnFocus : false,
36801     /**
36802      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36803      */
36804     blankText : "This field is required",
36805     /**
36806      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36807      * If available, this function will be called only after the basic validators all return true, and will be passed the
36808      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36809      */
36810     validator : null,
36811     /**
36812      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36813      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36814      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36815      */
36816     regex : null,
36817     /**
36818      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36819      */
36820     regexText : "",
36821     /**
36822      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
36823      */
36824     emptyText : null,
36825    
36826
36827     // private
36828     initEvents : function()
36829     {
36830         if (this.emptyText) {
36831             this.el.attr('placeholder', this.emptyText);
36832         }
36833         
36834         Roo.form.TextField.superclass.initEvents.call(this);
36835         if(this.validationEvent == 'keyup'){
36836             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36837             this.el.on('keyup', this.filterValidation, this);
36838         }
36839         else if(this.validationEvent !== false){
36840             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36841         }
36842         
36843         if(this.selectOnFocus){
36844             this.on("focus", this.preFocus, this);
36845             
36846         }
36847         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36848             this.el.on("keypress", this.filterKeys, this);
36849         }
36850         if(this.grow){
36851             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36852             this.el.on("click", this.autoSize,  this);
36853         }
36854         if(this.el.is('input[type=password]') && Roo.isSafari){
36855             this.el.on('keydown', this.SafariOnKeyDown, this);
36856         }
36857     },
36858
36859     processValue : function(value){
36860         if(this.stripCharsRe){
36861             var newValue = value.replace(this.stripCharsRe, '');
36862             if(newValue !== value){
36863                 this.setRawValue(newValue);
36864                 return newValue;
36865             }
36866         }
36867         return value;
36868     },
36869
36870     filterValidation : function(e){
36871         if(!e.isNavKeyPress()){
36872             this.validationTask.delay(this.validationDelay);
36873         }
36874     },
36875
36876     // private
36877     onKeyUp : function(e){
36878         if(!e.isNavKeyPress()){
36879             this.autoSize();
36880         }
36881     },
36882
36883     /**
36884      * Resets the current field value to the originally-loaded value and clears any validation messages.
36885      *  
36886      */
36887     reset : function(){
36888         Roo.form.TextField.superclass.reset.call(this);
36889        
36890     },
36891
36892     
36893     // private
36894     preFocus : function(){
36895         
36896         if(this.selectOnFocus){
36897             this.el.dom.select();
36898         }
36899     },
36900
36901     
36902     // private
36903     filterKeys : function(e){
36904         var k = e.getKey();
36905         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36906             return;
36907         }
36908         var c = e.getCharCode(), cc = String.fromCharCode(c);
36909         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36910             return;
36911         }
36912         if(!this.maskRe.test(cc)){
36913             e.stopEvent();
36914         }
36915     },
36916
36917     setValue : function(v){
36918         
36919         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36920         
36921         this.autoSize();
36922     },
36923
36924     /**
36925      * Validates a value according to the field's validation rules and marks the field as invalid
36926      * if the validation fails
36927      * @param {Mixed} value The value to validate
36928      * @return {Boolean} True if the value is valid, else false
36929      */
36930     validateValue : function(value){
36931         if(value.length < 1)  { // if it's blank
36932              if(this.allowBlank){
36933                 this.clearInvalid();
36934                 return true;
36935              }else{
36936                 this.markInvalid(this.blankText);
36937                 return false;
36938              }
36939         }
36940         if(value.length < this.minLength){
36941             this.markInvalid(String.format(this.minLengthText, this.minLength));
36942             return false;
36943         }
36944         if(value.length > this.maxLength){
36945             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36946             return false;
36947         }
36948         if(this.vtype){
36949             var vt = Roo.form.VTypes;
36950             if(!vt[this.vtype](value, this)){
36951                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36952                 return false;
36953             }
36954         }
36955         if(typeof this.validator == "function"){
36956             var msg = this.validator(value);
36957             if(msg !== true){
36958                 this.markInvalid(msg);
36959                 return false;
36960             }
36961         }
36962         if(this.regex && !this.regex.test(value)){
36963             this.markInvalid(this.regexText);
36964             return false;
36965         }
36966         return true;
36967     },
36968
36969     /**
36970      * Selects text in this field
36971      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36972      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36973      */
36974     selectText : function(start, end){
36975         var v = this.getRawValue();
36976         if(v.length > 0){
36977             start = start === undefined ? 0 : start;
36978             end = end === undefined ? v.length : end;
36979             var d = this.el.dom;
36980             if(d.setSelectionRange){
36981                 d.setSelectionRange(start, end);
36982             }else if(d.createTextRange){
36983                 var range = d.createTextRange();
36984                 range.moveStart("character", start);
36985                 range.moveEnd("character", v.length-end);
36986                 range.select();
36987             }
36988         }
36989     },
36990
36991     /**
36992      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36993      * This only takes effect if grow = true, and fires the autosize event.
36994      */
36995     autoSize : function(){
36996         if(!this.grow || !this.rendered){
36997             return;
36998         }
36999         if(!this.metrics){
37000             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37001         }
37002         var el = this.el;
37003         var v = el.dom.value;
37004         var d = document.createElement('div');
37005         d.appendChild(document.createTextNode(v));
37006         v = d.innerHTML;
37007         d = null;
37008         v += "&#160;";
37009         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37010         this.el.setWidth(w);
37011         this.fireEvent("autosize", this, w);
37012     },
37013     
37014     // private
37015     SafariOnKeyDown : function(event)
37016     {
37017         // this is a workaround for a password hang bug on chrome/ webkit.
37018         
37019         var isSelectAll = false;
37020         
37021         if(this.el.dom.selectionEnd > 0){
37022             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37023         }
37024         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37025             event.preventDefault();
37026             this.setValue('');
37027             return;
37028         }
37029         
37030         if(isSelectAll){ // backspace and delete key
37031             
37032             event.preventDefault();
37033             // this is very hacky as keydown always get's upper case.
37034             //
37035             var cc = String.fromCharCode(event.getCharCode());
37036             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37037             
37038         }
37039         
37040         
37041     }
37042 });/*
37043  * Based on:
37044  * Ext JS Library 1.1.1
37045  * Copyright(c) 2006-2007, Ext JS, LLC.
37046  *
37047  * Originally Released Under LGPL - original licence link has changed is not relivant.
37048  *
37049  * Fork - LGPL
37050  * <script type="text/javascript">
37051  */
37052  
37053 /**
37054  * @class Roo.form.Hidden
37055  * @extends Roo.form.TextField
37056  * Simple Hidden element used on forms 
37057  * 
37058  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37059  * 
37060  * @constructor
37061  * Creates a new Hidden form element.
37062  * @param {Object} config Configuration options
37063  */
37064
37065
37066
37067 // easy hidden field...
37068 Roo.form.Hidden = function(config){
37069     Roo.form.Hidden.superclass.constructor.call(this, config);
37070 };
37071   
37072 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37073     fieldLabel:      '',
37074     inputType:      'hidden',
37075     width:          50,
37076     allowBlank:     true,
37077     labelSeparator: '',
37078     hidden:         true,
37079     itemCls :       'x-form-item-display-none'
37080
37081
37082 });
37083
37084
37085 /*
37086  * Based on:
37087  * Ext JS Library 1.1.1
37088  * Copyright(c) 2006-2007, Ext JS, LLC.
37089  *
37090  * Originally Released Under LGPL - original licence link has changed is not relivant.
37091  *
37092  * Fork - LGPL
37093  * <script type="text/javascript">
37094  */
37095  
37096 /**
37097  * @class Roo.form.TriggerField
37098  * @extends Roo.form.TextField
37099  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37100  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37101  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37102  * for which you can provide a custom implementation.  For example:
37103  * <pre><code>
37104 var trigger = new Roo.form.TriggerField();
37105 trigger.onTriggerClick = myTriggerFn;
37106 trigger.applyTo('my-field');
37107 </code></pre>
37108  *
37109  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37110  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37111  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37112  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37113  * @constructor
37114  * Create a new TriggerField.
37115  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37116  * to the base TextField)
37117  */
37118 Roo.form.TriggerField = function(config){
37119     this.mimicing = false;
37120     Roo.form.TriggerField.superclass.constructor.call(this, config);
37121 };
37122
37123 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37124     /**
37125      * @cfg {String} triggerClass A CSS class to apply to the trigger
37126      */
37127     /**
37128      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37129      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37130      */
37131     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37132     /**
37133      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37134      */
37135     hideTrigger:false,
37136
37137     /** @cfg {Boolean} grow @hide */
37138     /** @cfg {Number} growMin @hide */
37139     /** @cfg {Number} growMax @hide */
37140
37141     /**
37142      * @hide 
37143      * @method
37144      */
37145     autoSize: Roo.emptyFn,
37146     // private
37147     monitorTab : true,
37148     // private
37149     deferHeight : true,
37150
37151     
37152     actionMode : 'wrap',
37153     // private
37154     onResize : function(w, h){
37155         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37156         if(typeof w == 'number'){
37157             var x = w - this.trigger.getWidth();
37158             this.el.setWidth(this.adjustWidth('input', x));
37159             this.trigger.setStyle('left', x+'px');
37160         }
37161     },
37162
37163     // private
37164     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37165
37166     // private
37167     getResizeEl : function(){
37168         return this.wrap;
37169     },
37170
37171     // private
37172     getPositionEl : function(){
37173         return this.wrap;
37174     },
37175
37176     // private
37177     alignErrorIcon : function(){
37178         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37179     },
37180
37181     // private
37182     onRender : function(ct, position){
37183         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37184         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37185         this.trigger = this.wrap.createChild(this.triggerConfig ||
37186                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37187         if(this.hideTrigger){
37188             this.trigger.setDisplayed(false);
37189         }
37190         this.initTrigger();
37191         if(!this.width){
37192             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37193         }
37194     },
37195
37196     // private
37197     initTrigger : function(){
37198         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37199         this.trigger.addClassOnOver('x-form-trigger-over');
37200         this.trigger.addClassOnClick('x-form-trigger-click');
37201     },
37202
37203     // private
37204     onDestroy : function(){
37205         if(this.trigger){
37206             this.trigger.removeAllListeners();
37207             this.trigger.remove();
37208         }
37209         if(this.wrap){
37210             this.wrap.remove();
37211         }
37212         Roo.form.TriggerField.superclass.onDestroy.call(this);
37213     },
37214
37215     // private
37216     onFocus : function(){
37217         Roo.form.TriggerField.superclass.onFocus.call(this);
37218         if(!this.mimicing){
37219             this.wrap.addClass('x-trigger-wrap-focus');
37220             this.mimicing = true;
37221             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37222             if(this.monitorTab){
37223                 this.el.on("keydown", this.checkTab, this);
37224             }
37225         }
37226     },
37227
37228     // private
37229     checkTab : function(e){
37230         if(e.getKey() == e.TAB){
37231             this.triggerBlur();
37232         }
37233     },
37234
37235     // private
37236     onBlur : function(){
37237         // do nothing
37238     },
37239
37240     // private
37241     mimicBlur : function(e, t){
37242         if(!this.wrap.contains(t) && this.validateBlur()){
37243             this.triggerBlur();
37244         }
37245     },
37246
37247     // private
37248     triggerBlur : function(){
37249         this.mimicing = false;
37250         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37251         if(this.monitorTab){
37252             this.el.un("keydown", this.checkTab, this);
37253         }
37254         this.wrap.removeClass('x-trigger-wrap-focus');
37255         Roo.form.TriggerField.superclass.onBlur.call(this);
37256     },
37257
37258     // private
37259     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37260     validateBlur : function(e, t){
37261         return true;
37262     },
37263
37264     // private
37265     onDisable : function(){
37266         Roo.form.TriggerField.superclass.onDisable.call(this);
37267         if(this.wrap){
37268             this.wrap.addClass('x-item-disabled');
37269         }
37270     },
37271
37272     // private
37273     onEnable : function(){
37274         Roo.form.TriggerField.superclass.onEnable.call(this);
37275         if(this.wrap){
37276             this.wrap.removeClass('x-item-disabled');
37277         }
37278     },
37279
37280     // private
37281     onShow : function(){
37282         var ae = this.getActionEl();
37283         
37284         if(ae){
37285             ae.dom.style.display = '';
37286             ae.dom.style.visibility = 'visible';
37287         }
37288     },
37289
37290     // private
37291     
37292     onHide : function(){
37293         var ae = this.getActionEl();
37294         ae.dom.style.display = 'none';
37295     },
37296
37297     /**
37298      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37299      * by an implementing function.
37300      * @method
37301      * @param {EventObject} e
37302      */
37303     onTriggerClick : Roo.emptyFn
37304 });
37305
37306 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37307 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37308 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37309 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37310     initComponent : function(){
37311         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37312
37313         this.triggerConfig = {
37314             tag:'span', cls:'x-form-twin-triggers', cn:[
37315             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37316             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37317         ]};
37318     },
37319
37320     getTrigger : function(index){
37321         return this.triggers[index];
37322     },
37323
37324     initTrigger : function(){
37325         var ts = this.trigger.select('.x-form-trigger', true);
37326         this.wrap.setStyle('overflow', 'hidden');
37327         var triggerField = this;
37328         ts.each(function(t, all, index){
37329             t.hide = function(){
37330                 var w = triggerField.wrap.getWidth();
37331                 this.dom.style.display = 'none';
37332                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37333             };
37334             t.show = function(){
37335                 var w = triggerField.wrap.getWidth();
37336                 this.dom.style.display = '';
37337                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37338             };
37339             var triggerIndex = 'Trigger'+(index+1);
37340
37341             if(this['hide'+triggerIndex]){
37342                 t.dom.style.display = 'none';
37343             }
37344             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37345             t.addClassOnOver('x-form-trigger-over');
37346             t.addClassOnClick('x-form-trigger-click');
37347         }, this);
37348         this.triggers = ts.elements;
37349     },
37350
37351     onTrigger1Click : Roo.emptyFn,
37352     onTrigger2Click : Roo.emptyFn
37353 });/*
37354  * Based on:
37355  * Ext JS Library 1.1.1
37356  * Copyright(c) 2006-2007, Ext JS, LLC.
37357  *
37358  * Originally Released Under LGPL - original licence link has changed is not relivant.
37359  *
37360  * Fork - LGPL
37361  * <script type="text/javascript">
37362  */
37363  
37364 /**
37365  * @class Roo.form.TextArea
37366  * @extends Roo.form.TextField
37367  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
37368  * support for auto-sizing.
37369  * @constructor
37370  * Creates a new TextArea
37371  * @param {Object} config Configuration options
37372  */
37373 Roo.form.TextArea = function(config){
37374     Roo.form.TextArea.superclass.constructor.call(this, config);
37375     // these are provided exchanges for backwards compat
37376     // minHeight/maxHeight were replaced by growMin/growMax to be
37377     // compatible with TextField growing config values
37378     if(this.minHeight !== undefined){
37379         this.growMin = this.minHeight;
37380     }
37381     if(this.maxHeight !== undefined){
37382         this.growMax = this.maxHeight;
37383     }
37384 };
37385
37386 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
37387     /**
37388      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
37389      */
37390     growMin : 60,
37391     /**
37392      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
37393      */
37394     growMax: 1000,
37395     /**
37396      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
37397      * in the field (equivalent to setting overflow: hidden, defaults to false)
37398      */
37399     preventScrollbars: false,
37400     /**
37401      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37402      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
37403      */
37404
37405     // private
37406     onRender : function(ct, position){
37407         if(!this.el){
37408             this.defaultAutoCreate = {
37409                 tag: "textarea",
37410                 style:"width:300px;height:60px;",
37411                 autocomplete: "off"
37412             };
37413         }
37414         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
37415         if(this.grow){
37416             this.textSizeEl = Roo.DomHelper.append(document.body, {
37417                 tag: "pre", cls: "x-form-grow-sizer"
37418             });
37419             if(this.preventScrollbars){
37420                 this.el.setStyle("overflow", "hidden");
37421             }
37422             this.el.setHeight(this.growMin);
37423         }
37424     },
37425
37426     onDestroy : function(){
37427         if(this.textSizeEl){
37428             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
37429         }
37430         Roo.form.TextArea.superclass.onDestroy.call(this);
37431     },
37432
37433     // private
37434     onKeyUp : function(e){
37435         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
37436             this.autoSize();
37437         }
37438     },
37439
37440     /**
37441      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
37442      * This only takes effect if grow = true, and fires the autosize event if the height changes.
37443      */
37444     autoSize : function(){
37445         if(!this.grow || !this.textSizeEl){
37446             return;
37447         }
37448         var el = this.el;
37449         var v = el.dom.value;
37450         var ts = this.textSizeEl;
37451
37452         ts.innerHTML = '';
37453         ts.appendChild(document.createTextNode(v));
37454         v = ts.innerHTML;
37455
37456         Roo.fly(ts).setWidth(this.el.getWidth());
37457         if(v.length < 1){
37458             v = "&#160;&#160;";
37459         }else{
37460             if(Roo.isIE){
37461                 v = v.replace(/\n/g, '<p>&#160;</p>');
37462             }
37463             v += "&#160;\n&#160;";
37464         }
37465         ts.innerHTML = v;
37466         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
37467         if(h != this.lastHeight){
37468             this.lastHeight = h;
37469             this.el.setHeight(h);
37470             this.fireEvent("autosize", this, h);
37471         }
37472     }
37473 });/*
37474  * Based on:
37475  * Ext JS Library 1.1.1
37476  * Copyright(c) 2006-2007, Ext JS, LLC.
37477  *
37478  * Originally Released Under LGPL - original licence link has changed is not relivant.
37479  *
37480  * Fork - LGPL
37481  * <script type="text/javascript">
37482  */
37483  
37484
37485 /**
37486  * @class Roo.form.NumberField
37487  * @extends Roo.form.TextField
37488  * Numeric text field that provides automatic keystroke filtering and numeric validation.
37489  * @constructor
37490  * Creates a new NumberField
37491  * @param {Object} config Configuration options
37492  */
37493 Roo.form.NumberField = function(config){
37494     Roo.form.NumberField.superclass.constructor.call(this, config);
37495 };
37496
37497 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
37498     /**
37499      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
37500      */
37501     fieldClass: "x-form-field x-form-num-field",
37502     /**
37503      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
37504      */
37505     allowDecimals : true,
37506     /**
37507      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
37508      */
37509     decimalSeparator : ".",
37510     /**
37511      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
37512      */
37513     decimalPrecision : 2,
37514     /**
37515      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
37516      */
37517     allowNegative : true,
37518     /**
37519      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
37520      */
37521     minValue : Number.NEGATIVE_INFINITY,
37522     /**
37523      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
37524      */
37525     maxValue : Number.MAX_VALUE,
37526     /**
37527      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
37528      */
37529     minText : "The minimum value for this field is {0}",
37530     /**
37531      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37532      */
37533     maxText : "The maximum value for this field is {0}",
37534     /**
37535      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
37536      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37537      */
37538     nanText : "{0} is not a valid number",
37539
37540     // private
37541     initEvents : function(){
37542         Roo.form.NumberField.superclass.initEvents.call(this);
37543         var allowed = "0123456789";
37544         if(this.allowDecimals){
37545             allowed += this.decimalSeparator;
37546         }
37547         if(this.allowNegative){
37548             allowed += "-";
37549         }
37550         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37551         var keyPress = function(e){
37552             var k = e.getKey();
37553             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37554                 return;
37555             }
37556             var c = e.getCharCode();
37557             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37558                 e.stopEvent();
37559             }
37560         };
37561         this.el.on("keypress", keyPress, this);
37562     },
37563
37564     // private
37565     validateValue : function(value){
37566         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
37567             return false;
37568         }
37569         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37570              return true;
37571         }
37572         var num = this.parseValue(value);
37573         if(isNaN(num)){
37574             this.markInvalid(String.format(this.nanText, value));
37575             return false;
37576         }
37577         if(num < this.minValue){
37578             this.markInvalid(String.format(this.minText, this.minValue));
37579             return false;
37580         }
37581         if(num > this.maxValue){
37582             this.markInvalid(String.format(this.maxText, this.maxValue));
37583             return false;
37584         }
37585         return true;
37586     },
37587
37588     getValue : function(){
37589         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
37590     },
37591
37592     // private
37593     parseValue : function(value){
37594         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37595         return isNaN(value) ? '' : value;
37596     },
37597
37598     // private
37599     fixPrecision : function(value){
37600         var nan = isNaN(value);
37601         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37602             return nan ? '' : value;
37603         }
37604         return parseFloat(value).toFixed(this.decimalPrecision);
37605     },
37606
37607     setValue : function(v){
37608         v = this.fixPrecision(v);
37609         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
37610     },
37611
37612     // private
37613     decimalPrecisionFcn : function(v){
37614         return Math.floor(v);
37615     },
37616
37617     beforeBlur : function(){
37618         var v = this.parseValue(this.getRawValue());
37619         if(v){
37620             this.setValue(v);
37621         }
37622     }
37623 });/*
37624  * Based on:
37625  * Ext JS Library 1.1.1
37626  * Copyright(c) 2006-2007, Ext JS, LLC.
37627  *
37628  * Originally Released Under LGPL - original licence link has changed is not relivant.
37629  *
37630  * Fork - LGPL
37631  * <script type="text/javascript">
37632  */
37633  
37634 /**
37635  * @class Roo.form.DateField
37636  * @extends Roo.form.TriggerField
37637  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37638 * @constructor
37639 * Create a new DateField
37640 * @param {Object} config
37641  */
37642 Roo.form.DateField = function(config){
37643     Roo.form.DateField.superclass.constructor.call(this, config);
37644     
37645       this.addEvents({
37646          
37647         /**
37648          * @event select
37649          * Fires when a date is selected
37650              * @param {Roo.form.DateField} combo This combo box
37651              * @param {Date} date The date selected
37652              */
37653         'select' : true
37654          
37655     });
37656     
37657     
37658     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37659     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37660     this.ddMatch = null;
37661     if(this.disabledDates){
37662         var dd = this.disabledDates;
37663         var re = "(?:";
37664         for(var i = 0; i < dd.length; i++){
37665             re += dd[i];
37666             if(i != dd.length-1) re += "|";
37667         }
37668         this.ddMatch = new RegExp(re + ")");
37669     }
37670 };
37671
37672 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
37673     /**
37674      * @cfg {String} format
37675      * The default date format string which can be overriden for localization support.  The format must be
37676      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37677      */
37678     format : "m/d/y",
37679     /**
37680      * @cfg {String} altFormats
37681      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37682      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37683      */
37684     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
37685     /**
37686      * @cfg {Array} disabledDays
37687      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37688      */
37689     disabledDays : null,
37690     /**
37691      * @cfg {String} disabledDaysText
37692      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37693      */
37694     disabledDaysText : "Disabled",
37695     /**
37696      * @cfg {Array} disabledDates
37697      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37698      * expression so they are very powerful. Some examples:
37699      * <ul>
37700      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37701      * <li>["03/08", "09/16"] would disable those days for every year</li>
37702      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37703      * <li>["03/../2006"] would disable every day in March 2006</li>
37704      * <li>["^03"] would disable every day in every March</li>
37705      * </ul>
37706      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37707      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37708      */
37709     disabledDates : null,
37710     /**
37711      * @cfg {String} disabledDatesText
37712      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37713      */
37714     disabledDatesText : "Disabled",
37715     /**
37716      * @cfg {Date/String} minValue
37717      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37718      * valid format (defaults to null).
37719      */
37720     minValue : null,
37721     /**
37722      * @cfg {Date/String} maxValue
37723      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37724      * valid format (defaults to null).
37725      */
37726     maxValue : null,
37727     /**
37728      * @cfg {String} minText
37729      * The error text to display when the date in the cell is before minValue (defaults to
37730      * 'The date in this field must be after {minValue}').
37731      */
37732     minText : "The date in this field must be equal to or after {0}",
37733     /**
37734      * @cfg {String} maxText
37735      * The error text to display when the date in the cell is after maxValue (defaults to
37736      * 'The date in this field must be before {maxValue}').
37737      */
37738     maxText : "The date in this field must be equal to or before {0}",
37739     /**
37740      * @cfg {String} invalidText
37741      * The error text to display when the date in the field is invalid (defaults to
37742      * '{value} is not a valid date - it must be in the format {format}').
37743      */
37744     invalidText : "{0} is not a valid date - it must be in the format {1}",
37745     /**
37746      * @cfg {String} triggerClass
37747      * An additional CSS class used to style the trigger button.  The trigger will always get the
37748      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37749      * which displays a calendar icon).
37750      */
37751     triggerClass : 'x-form-date-trigger',
37752     
37753
37754     /**
37755      * @cfg {Boolean} useIso
37756      * if enabled, then the date field will use a hidden field to store the 
37757      * real value as iso formated date. default (false)
37758      */ 
37759     useIso : false,
37760     /**
37761      * @cfg {String/Object} autoCreate
37762      * A DomHelper element spec, or true for a default element spec (defaults to
37763      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37764      */ 
37765     // private
37766     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37767     
37768     // private
37769     hiddenField: false,
37770     
37771     onRender : function(ct, position)
37772     {
37773         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37774         if (this.useIso) {
37775             //this.el.dom.removeAttribute('name'); 
37776             Roo.log("Changing name?");
37777             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
37778             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37779                     'before', true);
37780             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
37781             // prevent input submission
37782             this.hiddenName = this.name;
37783         }
37784             
37785             
37786     },
37787     
37788     // private
37789     validateValue : function(value)
37790     {
37791         value = this.formatDate(value);
37792         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37793             Roo.log('super failed');
37794             return false;
37795         }
37796         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37797              return true;
37798         }
37799         var svalue = value;
37800         value = this.parseDate(value);
37801         if(!value){
37802             Roo.log('parse date failed' + svalue);
37803             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37804             return false;
37805         }
37806         var time = value.getTime();
37807         if(this.minValue && time < this.minValue.getTime()){
37808             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37809             return false;
37810         }
37811         if(this.maxValue && time > this.maxValue.getTime()){
37812             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37813             return false;
37814         }
37815         if(this.disabledDays){
37816             var day = value.getDay();
37817             for(var i = 0; i < this.disabledDays.length; i++) {
37818                 if(day === this.disabledDays[i]){
37819                     this.markInvalid(this.disabledDaysText);
37820                     return false;
37821                 }
37822             }
37823         }
37824         var fvalue = this.formatDate(value);
37825         if(this.ddMatch && this.ddMatch.test(fvalue)){
37826             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37827             return false;
37828         }
37829         return true;
37830     },
37831
37832     // private
37833     // Provides logic to override the default TriggerField.validateBlur which just returns true
37834     validateBlur : function(){
37835         return !this.menu || !this.menu.isVisible();
37836     },
37837     
37838     getName: function()
37839     {
37840         // returns hidden if it's set..
37841         if (!this.rendered) {return ''};
37842         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
37843         
37844     },
37845
37846     /**
37847      * Returns the current date value of the date field.
37848      * @return {Date} The date value
37849      */
37850     getValue : function(){
37851         
37852         return  this.hiddenField ?
37853                 this.hiddenField.value :
37854                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37855     },
37856
37857     /**
37858      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37859      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37860      * (the default format used is "m/d/y").
37861      * <br />Usage:
37862      * <pre><code>
37863 //All of these calls set the same date value (May 4, 2006)
37864
37865 //Pass a date object:
37866 var dt = new Date('5/4/06');
37867 dateField.setValue(dt);
37868
37869 //Pass a date string (default format):
37870 dateField.setValue('5/4/06');
37871
37872 //Pass a date string (custom format):
37873 dateField.format = 'Y-m-d';
37874 dateField.setValue('2006-5-4');
37875 </code></pre>
37876      * @param {String/Date} date The date or valid date string
37877      */
37878     setValue : function(date){
37879         if (this.hiddenField) {
37880             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37881         }
37882         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37883         // make sure the value field is always stored as a date..
37884         this.value = this.parseDate(date);
37885         
37886         
37887     },
37888
37889     // private
37890     parseDate : function(value){
37891         if(!value || value instanceof Date){
37892             return value;
37893         }
37894         var v = Date.parseDate(value, this.format);
37895          if (!v && this.useIso) {
37896             v = Date.parseDate(value, 'Y-m-d');
37897         }
37898         if(!v && this.altFormats){
37899             if(!this.altFormatsArray){
37900                 this.altFormatsArray = this.altFormats.split("|");
37901             }
37902             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37903                 v = Date.parseDate(value, this.altFormatsArray[i]);
37904             }
37905         }
37906         return v;
37907     },
37908
37909     // private
37910     formatDate : function(date, fmt){
37911         return (!date || !(date instanceof Date)) ?
37912                date : date.dateFormat(fmt || this.format);
37913     },
37914
37915     // private
37916     menuListeners : {
37917         select: function(m, d){
37918             
37919             this.setValue(d);
37920             this.fireEvent('select', this, d);
37921         },
37922         show : function(){ // retain focus styling
37923             this.onFocus();
37924         },
37925         hide : function(){
37926             this.focus.defer(10, this);
37927             var ml = this.menuListeners;
37928             this.menu.un("select", ml.select,  this);
37929             this.menu.un("show", ml.show,  this);
37930             this.menu.un("hide", ml.hide,  this);
37931         }
37932     },
37933
37934     // private
37935     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37936     onTriggerClick : function(){
37937         if(this.disabled){
37938             return;
37939         }
37940         if(this.menu == null){
37941             this.menu = new Roo.menu.DateMenu();
37942         }
37943         Roo.apply(this.menu.picker,  {
37944             showClear: this.allowBlank,
37945             minDate : this.minValue,
37946             maxDate : this.maxValue,
37947             disabledDatesRE : this.ddMatch,
37948             disabledDatesText : this.disabledDatesText,
37949             disabledDays : this.disabledDays,
37950             disabledDaysText : this.disabledDaysText,
37951             format : this.useIso ? 'Y-m-d' : this.format,
37952             minText : String.format(this.minText, this.formatDate(this.minValue)),
37953             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37954         });
37955         this.menu.on(Roo.apply({}, this.menuListeners, {
37956             scope:this
37957         }));
37958         this.menu.picker.setValue(this.getValue() || new Date());
37959         this.menu.show(this.el, "tl-bl?");
37960     },
37961
37962     beforeBlur : function(){
37963         var v = this.parseDate(this.getRawValue());
37964         if(v){
37965             this.setValue(v);
37966         }
37967     }
37968
37969     /** @cfg {Boolean} grow @hide */
37970     /** @cfg {Number} growMin @hide */
37971     /** @cfg {Number} growMax @hide */
37972     /**
37973      * @hide
37974      * @method autoSize
37975      */
37976 });/*
37977  * Based on:
37978  * Ext JS Library 1.1.1
37979  * Copyright(c) 2006-2007, Ext JS, LLC.
37980  *
37981  * Originally Released Under LGPL - original licence link has changed is not relivant.
37982  *
37983  * Fork - LGPL
37984  * <script type="text/javascript">
37985  */
37986  
37987 /**
37988  * @class Roo.form.MonthField
37989  * @extends Roo.form.TriggerField
37990  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37991 * @constructor
37992 * Create a new MonthField
37993 * @param {Object} config
37994  */
37995 Roo.form.MonthField = function(config){
37996     
37997     Roo.form.MonthField.superclass.constructor.call(this, config);
37998     
37999       this.addEvents({
38000          
38001         /**
38002          * @event select
38003          * Fires when a date is selected
38004              * @param {Roo.form.MonthFieeld} combo This combo box
38005              * @param {Date} date The date selected
38006              */
38007         'select' : true
38008          
38009     });
38010     
38011     
38012     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38013     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38014     this.ddMatch = null;
38015     if(this.disabledDates){
38016         var dd = this.disabledDates;
38017         var re = "(?:";
38018         for(var i = 0; i < dd.length; i++){
38019             re += dd[i];
38020             if(i != dd.length-1) re += "|";
38021         }
38022         this.ddMatch = new RegExp(re + ")");
38023     }
38024 };
38025
38026 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38027     /**
38028      * @cfg {String} format
38029      * The default date format string which can be overriden for localization support.  The format must be
38030      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38031      */
38032     format : "M Y",
38033     /**
38034      * @cfg {String} altFormats
38035      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38036      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38037      */
38038     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38039     /**
38040      * @cfg {Array} disabledDays
38041      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38042      */
38043     disabledDays : [0,1,2,3,4,5,6],
38044     /**
38045      * @cfg {String} disabledDaysText
38046      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38047      */
38048     disabledDaysText : "Disabled",
38049     /**
38050      * @cfg {Array} disabledDates
38051      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38052      * expression so they are very powerful. Some examples:
38053      * <ul>
38054      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38055      * <li>["03/08", "09/16"] would disable those days for every year</li>
38056      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38057      * <li>["03/../2006"] would disable every day in March 2006</li>
38058      * <li>["^03"] would disable every day in every March</li>
38059      * </ul>
38060      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38061      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38062      */
38063     disabledDates : null,
38064     /**
38065      * @cfg {String} disabledDatesText
38066      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38067      */
38068     disabledDatesText : "Disabled",
38069     /**
38070      * @cfg {Date/String} minValue
38071      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38072      * valid format (defaults to null).
38073      */
38074     minValue : null,
38075     /**
38076      * @cfg {Date/String} maxValue
38077      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38078      * valid format (defaults to null).
38079      */
38080     maxValue : null,
38081     /**
38082      * @cfg {String} minText
38083      * The error text to display when the date in the cell is before minValue (defaults to
38084      * 'The date in this field must be after {minValue}').
38085      */
38086     minText : "The date in this field must be equal to or after {0}",
38087     /**
38088      * @cfg {String} maxTextf
38089      * The error text to display when the date in the cell is after maxValue (defaults to
38090      * 'The date in this field must be before {maxValue}').
38091      */
38092     maxText : "The date in this field must be equal to or before {0}",
38093     /**
38094      * @cfg {String} invalidText
38095      * The error text to display when the date in the field is invalid (defaults to
38096      * '{value} is not a valid date - it must be in the format {format}').
38097      */
38098     invalidText : "{0} is not a valid date - it must be in the format {1}",
38099     /**
38100      * @cfg {String} triggerClass
38101      * An additional CSS class used to style the trigger button.  The trigger will always get the
38102      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38103      * which displays a calendar icon).
38104      */
38105     triggerClass : 'x-form-date-trigger',
38106     
38107
38108     /**
38109      * @cfg {Boolean} useIso
38110      * if enabled, then the date field will use a hidden field to store the 
38111      * real value as iso formated date. default (true)
38112      */ 
38113     useIso : true,
38114     /**
38115      * @cfg {String/Object} autoCreate
38116      * A DomHelper element spec, or true for a default element spec (defaults to
38117      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38118      */ 
38119     // private
38120     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38121     
38122     // private
38123     hiddenField: false,
38124     
38125     hideMonthPicker : false,
38126     
38127     onRender : function(ct, position)
38128     {
38129         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38130         if (this.useIso) {
38131             this.el.dom.removeAttribute('name'); 
38132             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38133                     'before', true);
38134             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38135             // prevent input submission
38136             this.hiddenName = this.name;
38137         }
38138             
38139             
38140     },
38141     
38142     // private
38143     validateValue : function(value)
38144     {
38145         value = this.formatDate(value);
38146         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38147             return false;
38148         }
38149         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38150              return true;
38151         }
38152         var svalue = value;
38153         value = this.parseDate(value);
38154         if(!value){
38155             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38156             return false;
38157         }
38158         var time = value.getTime();
38159         if(this.minValue && time < this.minValue.getTime()){
38160             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38161             return false;
38162         }
38163         if(this.maxValue && time > this.maxValue.getTime()){
38164             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38165             return false;
38166         }
38167         /*if(this.disabledDays){
38168             var day = value.getDay();
38169             for(var i = 0; i < this.disabledDays.length; i++) {
38170                 if(day === this.disabledDays[i]){
38171                     this.markInvalid(this.disabledDaysText);
38172                     return false;
38173                 }
38174             }
38175         }
38176         */
38177         var fvalue = this.formatDate(value);
38178         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38179             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38180             return false;
38181         }
38182         */
38183         return true;
38184     },
38185
38186     // private
38187     // Provides logic to override the default TriggerField.validateBlur which just returns true
38188     validateBlur : function(){
38189         return !this.menu || !this.menu.isVisible();
38190     },
38191
38192     /**
38193      * Returns the current date value of the date field.
38194      * @return {Date} The date value
38195      */
38196     getValue : function(){
38197         
38198         
38199         
38200         return  this.hiddenField ?
38201                 this.hiddenField.value :
38202                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38203     },
38204
38205     /**
38206      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38207      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38208      * (the default format used is "m/d/y").
38209      * <br />Usage:
38210      * <pre><code>
38211 //All of these calls set the same date value (May 4, 2006)
38212
38213 //Pass a date object:
38214 var dt = new Date('5/4/06');
38215 monthField.setValue(dt);
38216
38217 //Pass a date string (default format):
38218 monthField.setValue('5/4/06');
38219
38220 //Pass a date string (custom format):
38221 monthField.format = 'Y-m-d';
38222 monthField.setValue('2006-5-4');
38223 </code></pre>
38224      * @param {String/Date} date The date or valid date string
38225      */
38226     setValue : function(date){
38227         Roo.log('month setValue' + date);
38228         // can only be first of month..
38229         
38230         var val = this.parseDate(date);
38231         
38232         if (this.hiddenField) {
38233             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38234         }
38235         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38236         this.value = this.parseDate(date);
38237     },
38238
38239     // private
38240     parseDate : function(value){
38241         if(!value || value instanceof Date){
38242             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38243             return value;
38244         }
38245         var v = Date.parseDate(value, this.format);
38246         if (!v && this.useIso) {
38247             v = Date.parseDate(value, 'Y-m-d');
38248         }
38249         if (v) {
38250             // 
38251             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38252         }
38253         
38254         
38255         if(!v && this.altFormats){
38256             if(!this.altFormatsArray){
38257                 this.altFormatsArray = this.altFormats.split("|");
38258             }
38259             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38260                 v = Date.parseDate(value, this.altFormatsArray[i]);
38261             }
38262         }
38263         return v;
38264     },
38265
38266     // private
38267     formatDate : function(date, fmt){
38268         return (!date || !(date instanceof Date)) ?
38269                date : date.dateFormat(fmt || this.format);
38270     },
38271
38272     // private
38273     menuListeners : {
38274         select: function(m, d){
38275             this.setValue(d);
38276             this.fireEvent('select', this, d);
38277         },
38278         show : function(){ // retain focus styling
38279             this.onFocus();
38280         },
38281         hide : function(){
38282             this.focus.defer(10, this);
38283             var ml = this.menuListeners;
38284             this.menu.un("select", ml.select,  this);
38285             this.menu.un("show", ml.show,  this);
38286             this.menu.un("hide", ml.hide,  this);
38287         }
38288     },
38289     // private
38290     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38291     onTriggerClick : function(){
38292         if(this.disabled){
38293             return;
38294         }
38295         if(this.menu == null){
38296             this.menu = new Roo.menu.DateMenu();
38297            
38298         }
38299         
38300         Roo.apply(this.menu.picker,  {
38301             
38302             showClear: this.allowBlank,
38303             minDate : this.minValue,
38304             maxDate : this.maxValue,
38305             disabledDatesRE : this.ddMatch,
38306             disabledDatesText : this.disabledDatesText,
38307             
38308             format : this.useIso ? 'Y-m-d' : this.format,
38309             minText : String.format(this.minText, this.formatDate(this.minValue)),
38310             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38311             
38312         });
38313          this.menu.on(Roo.apply({}, this.menuListeners, {
38314             scope:this
38315         }));
38316        
38317         
38318         var m = this.menu;
38319         var p = m.picker;
38320         
38321         // hide month picker get's called when we called by 'before hide';
38322         
38323         var ignorehide = true;
38324         p.hideMonthPicker  = function(disableAnim){
38325             if (ignorehide) {
38326                 return;
38327             }
38328              if(this.monthPicker){
38329                 Roo.log("hideMonthPicker called");
38330                 if(disableAnim === true){
38331                     this.monthPicker.hide();
38332                 }else{
38333                     this.monthPicker.slideOut('t', {duration:.2});
38334                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38335                     p.fireEvent("select", this, this.value);
38336                     m.hide();
38337                 }
38338             }
38339         }
38340         
38341         Roo.log('picker set value');
38342         Roo.log(this.getValue());
38343         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
38344         m.show(this.el, 'tl-bl?');
38345         ignorehide  = false;
38346         // this will trigger hideMonthPicker..
38347         
38348         
38349         // hidden the day picker
38350         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
38351         
38352         
38353         
38354       
38355         
38356         p.showMonthPicker.defer(100, p);
38357     
38358         
38359        
38360     },
38361
38362     beforeBlur : function(){
38363         var v = this.parseDate(this.getRawValue());
38364         if(v){
38365             this.setValue(v);
38366         }
38367     }
38368
38369     /** @cfg {Boolean} grow @hide */
38370     /** @cfg {Number} growMin @hide */
38371     /** @cfg {Number} growMax @hide */
38372     /**
38373      * @hide
38374      * @method autoSize
38375      */
38376 });/*
38377  * Based on:
38378  * Ext JS Library 1.1.1
38379  * Copyright(c) 2006-2007, Ext JS, LLC.
38380  *
38381  * Originally Released Under LGPL - original licence link has changed is not relivant.
38382  *
38383  * Fork - LGPL
38384  * <script type="text/javascript">
38385  */
38386  
38387
38388 /**
38389  * @class Roo.form.ComboBox
38390  * @extends Roo.form.TriggerField
38391  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
38392  * @constructor
38393  * Create a new ComboBox.
38394  * @param {Object} config Configuration options
38395  */
38396 Roo.form.ComboBox = function(config){
38397     Roo.form.ComboBox.superclass.constructor.call(this, config);
38398     this.addEvents({
38399         /**
38400          * @event expand
38401          * Fires when the dropdown list is expanded
38402              * @param {Roo.form.ComboBox} combo This combo box
38403              */
38404         'expand' : true,
38405         /**
38406          * @event collapse
38407          * Fires when the dropdown list is collapsed
38408              * @param {Roo.form.ComboBox} combo This combo box
38409              */
38410         'collapse' : true,
38411         /**
38412          * @event beforeselect
38413          * Fires before a list item is selected. Return false to cancel the selection.
38414              * @param {Roo.form.ComboBox} combo This combo box
38415              * @param {Roo.data.Record} record The data record returned from the underlying store
38416              * @param {Number} index The index of the selected item in the dropdown list
38417              */
38418         'beforeselect' : true,
38419         /**
38420          * @event select
38421          * Fires when a list item is selected
38422              * @param {Roo.form.ComboBox} combo This combo box
38423              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
38424              * @param {Number} index The index of the selected item in the dropdown list
38425              */
38426         'select' : true,
38427         /**
38428          * @event beforequery
38429          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
38430          * The event object passed has these properties:
38431              * @param {Roo.form.ComboBox} combo This combo box
38432              * @param {String} query The query
38433              * @param {Boolean} forceAll true to force "all" query
38434              * @param {Boolean} cancel true to cancel the query
38435              * @param {Object} e The query event object
38436              */
38437         'beforequery': true,
38438          /**
38439          * @event add
38440          * Fires when the 'add' icon is pressed (add a listener to enable add button)
38441              * @param {Roo.form.ComboBox} combo This combo box
38442              */
38443         'add' : true,
38444         /**
38445          * @event edit
38446          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
38447              * @param {Roo.form.ComboBox} combo This combo box
38448              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
38449              */
38450         'edit' : true
38451         
38452         
38453     });
38454     if(this.transform){
38455         this.allowDomMove = false;
38456         var s = Roo.getDom(this.transform);
38457         if(!this.hiddenName){
38458             this.hiddenName = s.name;
38459         }
38460         if(!this.store){
38461             this.mode = 'local';
38462             var d = [], opts = s.options;
38463             for(var i = 0, len = opts.length;i < len; i++){
38464                 var o = opts[i];
38465                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
38466                 if(o.selected) {
38467                     this.value = value;
38468                 }
38469                 d.push([value, o.text]);
38470             }
38471             this.store = new Roo.data.SimpleStore({
38472                 'id': 0,
38473                 fields: ['value', 'text'],
38474                 data : d
38475             });
38476             this.valueField = 'value';
38477             this.displayField = 'text';
38478         }
38479         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
38480         if(!this.lazyRender){
38481             this.target = true;
38482             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
38483             s.parentNode.removeChild(s); // remove it
38484             this.render(this.el.parentNode);
38485         }else{
38486             s.parentNode.removeChild(s); // remove it
38487         }
38488
38489     }
38490     if (this.store) {
38491         this.store = Roo.factory(this.store, Roo.data);
38492     }
38493     
38494     this.selectedIndex = -1;
38495     if(this.mode == 'local'){
38496         if(config.queryDelay === undefined){
38497             this.queryDelay = 10;
38498         }
38499         if(config.minChars === undefined){
38500             this.minChars = 0;
38501         }
38502     }
38503 };
38504
38505 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
38506     /**
38507      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
38508      */
38509     /**
38510      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
38511      * rendering into an Roo.Editor, defaults to false)
38512      */
38513     /**
38514      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
38515      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
38516      */
38517     /**
38518      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
38519      */
38520     /**
38521      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
38522      * the dropdown list (defaults to undefined, with no header element)
38523      */
38524
38525      /**
38526      * @cfg {String/Roo.Template} tpl The template to use to render the output
38527      */
38528      
38529     // private
38530     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
38531     /**
38532      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
38533      */
38534     listWidth: undefined,
38535     /**
38536      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
38537      * mode = 'remote' or 'text' if mode = 'local')
38538      */
38539     displayField: undefined,
38540     /**
38541      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
38542      * mode = 'remote' or 'value' if mode = 'local'). 
38543      * Note: use of a valueField requires the user make a selection
38544      * in order for a value to be mapped.
38545      */
38546     valueField: undefined,
38547     
38548     
38549     /**
38550      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
38551      * field's data value (defaults to the underlying DOM element's name)
38552      */
38553     hiddenName: undefined,
38554     /**
38555      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
38556      */
38557     listClass: '',
38558     /**
38559      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
38560      */
38561     selectedClass: 'x-combo-selected',
38562     /**
38563      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38564      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
38565      * which displays a downward arrow icon).
38566      */
38567     triggerClass : 'x-form-arrow-trigger',
38568     /**
38569      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
38570      */
38571     shadow:'sides',
38572     /**
38573      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
38574      * anchor positions (defaults to 'tl-bl')
38575      */
38576     listAlign: 'tl-bl?',
38577     /**
38578      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
38579      */
38580     maxHeight: 300,
38581     /**
38582      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
38583      * query specified by the allQuery config option (defaults to 'query')
38584      */
38585     triggerAction: 'query',
38586     /**
38587      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
38588      * (defaults to 4, does not apply if editable = false)
38589      */
38590     minChars : 4,
38591     /**
38592      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
38593      * delay (typeAheadDelay) if it matches a known value (defaults to false)
38594      */
38595     typeAhead: false,
38596     /**
38597      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
38598      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
38599      */
38600     queryDelay: 500,
38601     /**
38602      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
38603      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
38604      */
38605     pageSize: 0,
38606     /**
38607      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
38608      * when editable = true (defaults to false)
38609      */
38610     selectOnFocus:false,
38611     /**
38612      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
38613      */
38614     queryParam: 'query',
38615     /**
38616      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
38617      * when mode = 'remote' (defaults to 'Loading...')
38618      */
38619     loadingText: 'Loading...',
38620     /**
38621      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
38622      */
38623     resizable: false,
38624     /**
38625      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
38626      */
38627     handleHeight : 8,
38628     /**
38629      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
38630      * traditional select (defaults to true)
38631      */
38632     editable: true,
38633     /**
38634      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
38635      */
38636     allQuery: '',
38637     /**
38638      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
38639      */
38640     mode: 'remote',
38641     /**
38642      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
38643      * listWidth has a higher value)
38644      */
38645     minListWidth : 70,
38646     /**
38647      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
38648      * allow the user to set arbitrary text into the field (defaults to false)
38649      */
38650     forceSelection:false,
38651     /**
38652      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
38653      * if typeAhead = true (defaults to 250)
38654      */
38655     typeAheadDelay : 250,
38656     /**
38657      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
38658      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
38659      */
38660     valueNotFoundText : undefined,
38661     /**
38662      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
38663      */
38664     blockFocus : false,
38665     
38666     /**
38667      * @cfg {Boolean} disableClear Disable showing of clear button.
38668      */
38669     disableClear : false,
38670     /**
38671      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
38672      */
38673     alwaysQuery : false,
38674     
38675     //private
38676     addicon : false,
38677     editicon: false,
38678     
38679     // element that contains real text value.. (when hidden is used..)
38680      
38681     // private
38682     onRender : function(ct, position){
38683         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
38684         if(this.hiddenName){
38685             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
38686                     'before', true);
38687             this.hiddenField.value =
38688                 this.hiddenValue !== undefined ? this.hiddenValue :
38689                 this.value !== undefined ? this.value : '';
38690
38691             // prevent input submission
38692             this.el.dom.removeAttribute('name');
38693              
38694              
38695         }
38696         if(Roo.isGecko){
38697             this.el.dom.setAttribute('autocomplete', 'off');
38698         }
38699
38700         var cls = 'x-combo-list';
38701
38702         this.list = new Roo.Layer({
38703             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
38704         });
38705
38706         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
38707         this.list.setWidth(lw);
38708         this.list.swallowEvent('mousewheel');
38709         this.assetHeight = 0;
38710
38711         if(this.title){
38712             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
38713             this.assetHeight += this.header.getHeight();
38714         }
38715
38716         this.innerList = this.list.createChild({cls:cls+'-inner'});
38717         this.innerList.on('mouseover', this.onViewOver, this);
38718         this.innerList.on('mousemove', this.onViewMove, this);
38719         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38720         
38721         if(this.allowBlank && !this.pageSize && !this.disableClear){
38722             this.footer = this.list.createChild({cls:cls+'-ft'});
38723             this.pageTb = new Roo.Toolbar(this.footer);
38724            
38725         }
38726         if(this.pageSize){
38727             this.footer = this.list.createChild({cls:cls+'-ft'});
38728             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
38729                     {pageSize: this.pageSize});
38730             
38731         }
38732         
38733         if (this.pageTb && this.allowBlank && !this.disableClear) {
38734             var _this = this;
38735             this.pageTb.add(new Roo.Toolbar.Fill(), {
38736                 cls: 'x-btn-icon x-btn-clear',
38737                 text: '&#160;',
38738                 handler: function()
38739                 {
38740                     _this.collapse();
38741                     _this.clearValue();
38742                     _this.onSelect(false, -1);
38743                 }
38744             });
38745         }
38746         if (this.footer) {
38747             this.assetHeight += this.footer.getHeight();
38748         }
38749         
38750
38751         if(!this.tpl){
38752             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
38753         }
38754
38755         this.view = new Roo.View(this.innerList, this.tpl, {
38756             singleSelect:true, store: this.store, selectedClass: this.selectedClass
38757         });
38758
38759         this.view.on('click', this.onViewClick, this);
38760
38761         this.store.on('beforeload', this.onBeforeLoad, this);
38762         this.store.on('load', this.onLoad, this);
38763         this.store.on('loadexception', this.onLoadException, this);
38764
38765         if(this.resizable){
38766             this.resizer = new Roo.Resizable(this.list,  {
38767                pinned:true, handles:'se'
38768             });
38769             this.resizer.on('resize', function(r, w, h){
38770                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
38771                 this.listWidth = w;
38772                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
38773                 this.restrictHeight();
38774             }, this);
38775             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
38776         }
38777         if(!this.editable){
38778             this.editable = true;
38779             this.setEditable(false);
38780         }  
38781         
38782         
38783         if (typeof(this.events.add.listeners) != 'undefined') {
38784             
38785             this.addicon = this.wrap.createChild(
38786                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
38787        
38788             this.addicon.on('click', function(e) {
38789                 this.fireEvent('add', this);
38790             }, this);
38791         }
38792         if (typeof(this.events.edit.listeners) != 'undefined') {
38793             
38794             this.editicon = this.wrap.createChild(
38795                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
38796             if (this.addicon) {
38797                 this.editicon.setStyle('margin-left', '40px');
38798             }
38799             this.editicon.on('click', function(e) {
38800                 
38801                 // we fire even  if inothing is selected..
38802                 this.fireEvent('edit', this, this.lastData );
38803                 
38804             }, this);
38805         }
38806         
38807         
38808         
38809     },
38810
38811     // private
38812     initEvents : function(){
38813         Roo.form.ComboBox.superclass.initEvents.call(this);
38814
38815         this.keyNav = new Roo.KeyNav(this.el, {
38816             "up" : function(e){
38817                 this.inKeyMode = true;
38818                 this.selectPrev();
38819             },
38820
38821             "down" : function(e){
38822                 if(!this.isExpanded()){
38823                     this.onTriggerClick();
38824                 }else{
38825                     this.inKeyMode = true;
38826                     this.selectNext();
38827                 }
38828             },
38829
38830             "enter" : function(e){
38831                 this.onViewClick();
38832                 //return true;
38833             },
38834
38835             "esc" : function(e){
38836                 this.collapse();
38837             },
38838
38839             "tab" : function(e){
38840                 this.onViewClick(false);
38841                 this.fireEvent("specialkey", this, e);
38842                 return true;
38843             },
38844
38845             scope : this,
38846
38847             doRelay : function(foo, bar, hname){
38848                 if(hname == 'down' || this.scope.isExpanded()){
38849                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38850                 }
38851                 return true;
38852             },
38853
38854             forceKeyDown: true
38855         });
38856         this.queryDelay = Math.max(this.queryDelay || 10,
38857                 this.mode == 'local' ? 10 : 250);
38858         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
38859         if(this.typeAhead){
38860             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
38861         }
38862         if(this.editable !== false){
38863             this.el.on("keyup", this.onKeyUp, this);
38864         }
38865         if(this.forceSelection){
38866             this.on('blur', this.doForce, this);
38867         }
38868     },
38869
38870     onDestroy : function(){
38871         if(this.view){
38872             this.view.setStore(null);
38873             this.view.el.removeAllListeners();
38874             this.view.el.remove();
38875             this.view.purgeListeners();
38876         }
38877         if(this.list){
38878             this.list.destroy();
38879         }
38880         if(this.store){
38881             this.store.un('beforeload', this.onBeforeLoad, this);
38882             this.store.un('load', this.onLoad, this);
38883             this.store.un('loadexception', this.onLoadException, this);
38884         }
38885         Roo.form.ComboBox.superclass.onDestroy.call(this);
38886     },
38887
38888     // private
38889     fireKey : function(e){
38890         if(e.isNavKeyPress() && !this.list.isVisible()){
38891             this.fireEvent("specialkey", this, e);
38892         }
38893     },
38894
38895     // private
38896     onResize: function(w, h){
38897         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
38898         
38899         if(typeof w != 'number'){
38900             // we do not handle it!?!?
38901             return;
38902         }
38903         var tw = this.trigger.getWidth();
38904         tw += this.addicon ? this.addicon.getWidth() : 0;
38905         tw += this.editicon ? this.editicon.getWidth() : 0;
38906         var x = w - tw;
38907         this.el.setWidth( this.adjustWidth('input', x));
38908             
38909         this.trigger.setStyle('left', x+'px');
38910         
38911         if(this.list && this.listWidth === undefined){
38912             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
38913             this.list.setWidth(lw);
38914             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38915         }
38916         
38917     
38918         
38919     },
38920
38921     /**
38922      * Allow or prevent the user from directly editing the field text.  If false is passed,
38923      * the user will only be able to select from the items defined in the dropdown list.  This method
38924      * is the runtime equivalent of setting the 'editable' config option at config time.
38925      * @param {Boolean} value True to allow the user to directly edit the field text
38926      */
38927     setEditable : function(value){
38928         if(value == this.editable){
38929             return;
38930         }
38931         this.editable = value;
38932         if(!value){
38933             this.el.dom.setAttribute('readOnly', true);
38934             this.el.on('mousedown', this.onTriggerClick,  this);
38935             this.el.addClass('x-combo-noedit');
38936         }else{
38937             this.el.dom.setAttribute('readOnly', false);
38938             this.el.un('mousedown', this.onTriggerClick,  this);
38939             this.el.removeClass('x-combo-noedit');
38940         }
38941     },
38942
38943     // private
38944     onBeforeLoad : function(){
38945         if(!this.hasFocus){
38946             return;
38947         }
38948         this.innerList.update(this.loadingText ?
38949                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
38950         this.restrictHeight();
38951         this.selectedIndex = -1;
38952     },
38953
38954     // private
38955     onLoad : function(){
38956         if(!this.hasFocus){
38957             return;
38958         }
38959         if(this.store.getCount() > 0){
38960             this.expand();
38961             this.restrictHeight();
38962             if(this.lastQuery == this.allQuery){
38963                 if(this.editable){
38964                     this.el.dom.select();
38965                 }
38966                 if(!this.selectByValue(this.value, true)){
38967                     this.select(0, true);
38968                 }
38969             }else{
38970                 this.selectNext();
38971                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
38972                     this.taTask.delay(this.typeAheadDelay);
38973                 }
38974             }
38975         }else{
38976             this.onEmptyResults();
38977         }
38978         //this.el.focus();
38979     },
38980     // private
38981     onLoadException : function()
38982     {
38983         this.collapse();
38984         Roo.log(this.store.reader.jsonData);
38985         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38986             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38987         }
38988         
38989         
38990     },
38991     // private
38992     onTypeAhead : function(){
38993         if(this.store.getCount() > 0){
38994             var r = this.store.getAt(0);
38995             var newValue = r.data[this.displayField];
38996             var len = newValue.length;
38997             var selStart = this.getRawValue().length;
38998             if(selStart != len){
38999                 this.setRawValue(newValue);
39000                 this.selectText(selStart, newValue.length);
39001             }
39002         }
39003     },
39004
39005     // private
39006     onSelect : function(record, index){
39007         if(this.fireEvent('beforeselect', this, record, index) !== false){
39008             this.setFromData(index > -1 ? record.data : false);
39009             this.collapse();
39010             this.fireEvent('select', this, record, index);
39011         }
39012     },
39013
39014     /**
39015      * Returns the currently selected field value or empty string if no value is set.
39016      * @return {String} value The selected value
39017      */
39018     getValue : function(){
39019         if(this.valueField){
39020             return typeof this.value != 'undefined' ? this.value : '';
39021         }else{
39022             return Roo.form.ComboBox.superclass.getValue.call(this);
39023         }
39024     },
39025
39026     /**
39027      * Clears any text/value currently set in the field
39028      */
39029     clearValue : function(){
39030         if(this.hiddenField){
39031             this.hiddenField.value = '';
39032         }
39033         this.value = '';
39034         this.setRawValue('');
39035         this.lastSelectionText = '';
39036         
39037     },
39038
39039     /**
39040      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39041      * will be displayed in the field.  If the value does not match the data value of an existing item,
39042      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39043      * Otherwise the field will be blank (although the value will still be set).
39044      * @param {String} value The value to match
39045      */
39046     setValue : function(v){
39047         var text = v;
39048         if(this.valueField){
39049             var r = this.findRecord(this.valueField, v);
39050             if(r){
39051                 text = r.data[this.displayField];
39052             }else if(this.valueNotFoundText !== undefined){
39053                 text = this.valueNotFoundText;
39054             }
39055         }
39056         this.lastSelectionText = text;
39057         if(this.hiddenField){
39058             this.hiddenField.value = v;
39059         }
39060         Roo.form.ComboBox.superclass.setValue.call(this, text);
39061         this.value = v;
39062     },
39063     /**
39064      * @property {Object} the last set data for the element
39065      */
39066     
39067     lastData : false,
39068     /**
39069      * Sets the value of the field based on a object which is related to the record format for the store.
39070      * @param {Object} value the value to set as. or false on reset?
39071      */
39072     setFromData : function(o){
39073         var dv = ''; // display value
39074         var vv = ''; // value value..
39075         this.lastData = o;
39076         if (this.displayField) {
39077             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39078         } else {
39079             // this is an error condition!!!
39080             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39081         }
39082         
39083         if(this.valueField){
39084             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39085         }
39086         if(this.hiddenField){
39087             this.hiddenField.value = vv;
39088             
39089             this.lastSelectionText = dv;
39090             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39091             this.value = vv;
39092             return;
39093         }
39094         // no hidden field.. - we store the value in 'value', but still display
39095         // display field!!!!
39096         this.lastSelectionText = dv;
39097         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39098         this.value = vv;
39099         
39100         
39101     },
39102     // private
39103     reset : function(){
39104         // overridden so that last data is reset..
39105         this.setValue(this.originalValue);
39106         this.clearInvalid();
39107         this.lastData = false;
39108         if (this.view) {
39109             this.view.clearSelections();
39110         }
39111     },
39112     // private
39113     findRecord : function(prop, value){
39114         var record;
39115         if(this.store.getCount() > 0){
39116             this.store.each(function(r){
39117                 if(r.data[prop] == value){
39118                     record = r;
39119                     return false;
39120                 }
39121                 return true;
39122             });
39123         }
39124         return record;
39125     },
39126     
39127     getName: function()
39128     {
39129         // returns hidden if it's set..
39130         if (!this.rendered) {return ''};
39131         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39132         
39133     },
39134     // private
39135     onViewMove : function(e, t){
39136         this.inKeyMode = false;
39137     },
39138
39139     // private
39140     onViewOver : function(e, t){
39141         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39142             return;
39143         }
39144         var item = this.view.findItemFromChild(t);
39145         if(item){
39146             var index = this.view.indexOf(item);
39147             this.select(index, false);
39148         }
39149     },
39150
39151     // private
39152     onViewClick : function(doFocus)
39153     {
39154         var index = this.view.getSelectedIndexes()[0];
39155         var r = this.store.getAt(index);
39156         if(r){
39157             this.onSelect(r, index);
39158         }
39159         if(doFocus !== false && !this.blockFocus){
39160             this.el.focus();
39161         }
39162     },
39163
39164     // private
39165     restrictHeight : function(){
39166         this.innerList.dom.style.height = '';
39167         var inner = this.innerList.dom;
39168         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39169         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39170         this.list.beginUpdate();
39171         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39172         this.list.alignTo(this.el, this.listAlign);
39173         this.list.endUpdate();
39174     },
39175
39176     // private
39177     onEmptyResults : function(){
39178         this.collapse();
39179     },
39180
39181     /**
39182      * Returns true if the dropdown list is expanded, else false.
39183      */
39184     isExpanded : function(){
39185         return this.list.isVisible();
39186     },
39187
39188     /**
39189      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39190      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39191      * @param {String} value The data value of the item to select
39192      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39193      * selected item if it is not currently in view (defaults to true)
39194      * @return {Boolean} True if the value matched an item in the list, else false
39195      */
39196     selectByValue : function(v, scrollIntoView){
39197         if(v !== undefined && v !== null){
39198             var r = this.findRecord(this.valueField || this.displayField, v);
39199             if(r){
39200                 this.select(this.store.indexOf(r), scrollIntoView);
39201                 return true;
39202             }
39203         }
39204         return false;
39205     },
39206
39207     /**
39208      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39209      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39210      * @param {Number} index The zero-based index of the list item to select
39211      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39212      * selected item if it is not currently in view (defaults to true)
39213      */
39214     select : function(index, scrollIntoView){
39215         this.selectedIndex = index;
39216         this.view.select(index);
39217         if(scrollIntoView !== false){
39218             var el = this.view.getNode(index);
39219             if(el){
39220                 this.innerList.scrollChildIntoView(el, false);
39221             }
39222         }
39223     },
39224
39225     // private
39226     selectNext : function(){
39227         var ct = this.store.getCount();
39228         if(ct > 0){
39229             if(this.selectedIndex == -1){
39230                 this.select(0);
39231             }else if(this.selectedIndex < ct-1){
39232                 this.select(this.selectedIndex+1);
39233             }
39234         }
39235     },
39236
39237     // private
39238     selectPrev : function(){
39239         var ct = this.store.getCount();
39240         if(ct > 0){
39241             if(this.selectedIndex == -1){
39242                 this.select(0);
39243             }else if(this.selectedIndex != 0){
39244                 this.select(this.selectedIndex-1);
39245             }
39246         }
39247     },
39248
39249     // private
39250     onKeyUp : function(e){
39251         if(this.editable !== false && !e.isSpecialKey()){
39252             this.lastKey = e.getKey();
39253             this.dqTask.delay(this.queryDelay);
39254         }
39255     },
39256
39257     // private
39258     validateBlur : function(){
39259         return !this.list || !this.list.isVisible();   
39260     },
39261
39262     // private
39263     initQuery : function(){
39264         this.doQuery(this.getRawValue());
39265     },
39266
39267     // private
39268     doForce : function(){
39269         if(this.el.dom.value.length > 0){
39270             this.el.dom.value =
39271                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39272              
39273         }
39274     },
39275
39276     /**
39277      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39278      * query allowing the query action to be canceled if needed.
39279      * @param {String} query The SQL query to execute
39280      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39281      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39282      * saved in the current store (defaults to false)
39283      */
39284     doQuery : function(q, forceAll){
39285         if(q === undefined || q === null){
39286             q = '';
39287         }
39288         var qe = {
39289             query: q,
39290             forceAll: forceAll,
39291             combo: this,
39292             cancel:false
39293         };
39294         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39295             return false;
39296         }
39297         q = qe.query;
39298         forceAll = qe.forceAll;
39299         if(forceAll === true || (q.length >= this.minChars)){
39300             if(this.lastQuery != q || this.alwaysQuery){
39301                 this.lastQuery = q;
39302                 if(this.mode == 'local'){
39303                     this.selectedIndex = -1;
39304                     if(forceAll){
39305                         this.store.clearFilter();
39306                     }else{
39307                         this.store.filter(this.displayField, q);
39308                     }
39309                     this.onLoad();
39310                 }else{
39311                     this.store.baseParams[this.queryParam] = q;
39312                     this.store.load({
39313                         params: this.getParams(q)
39314                     });
39315                     this.expand();
39316                 }
39317             }else{
39318                 this.selectedIndex = -1;
39319                 this.onLoad();   
39320             }
39321         }
39322     },
39323
39324     // private
39325     getParams : function(q){
39326         var p = {};
39327         //p[this.queryParam] = q;
39328         if(this.pageSize){
39329             p.start = 0;
39330             p.limit = this.pageSize;
39331         }
39332         return p;
39333     },
39334
39335     /**
39336      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39337      */
39338     collapse : function(){
39339         if(!this.isExpanded()){
39340             return;
39341         }
39342         this.list.hide();
39343         Roo.get(document).un('mousedown', this.collapseIf, this);
39344         Roo.get(document).un('mousewheel', this.collapseIf, this);
39345         if (!this.editable) {
39346             Roo.get(document).un('keydown', this.listKeyPress, this);
39347         }
39348         this.fireEvent('collapse', this);
39349     },
39350
39351     // private
39352     collapseIf : function(e){
39353         if(!e.within(this.wrap) && !e.within(this.list)){
39354             this.collapse();
39355         }
39356     },
39357
39358     /**
39359      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39360      */
39361     expand : function(){
39362         if(this.isExpanded() || !this.hasFocus){
39363             return;
39364         }
39365         this.list.alignTo(this.el, this.listAlign);
39366         this.list.show();
39367         Roo.get(document).on('mousedown', this.collapseIf, this);
39368         Roo.get(document).on('mousewheel', this.collapseIf, this);
39369         if (!this.editable) {
39370             Roo.get(document).on('keydown', this.listKeyPress, this);
39371         }
39372         
39373         this.fireEvent('expand', this);
39374     },
39375
39376     // private
39377     // Implements the default empty TriggerField.onTriggerClick function
39378     onTriggerClick : function(){
39379         if(this.disabled){
39380             return;
39381         }
39382         if(this.isExpanded()){
39383             this.collapse();
39384             if (!this.blockFocus) {
39385                 this.el.focus();
39386             }
39387             
39388         }else {
39389             this.hasFocus = true;
39390             if(this.triggerAction == 'all') {
39391                 this.doQuery(this.allQuery, true);
39392             } else {
39393                 this.doQuery(this.getRawValue());
39394             }
39395             if (!this.blockFocus) {
39396                 this.el.focus();
39397             }
39398         }
39399     },
39400     listKeyPress : function(e)
39401     {
39402         //Roo.log('listkeypress');
39403         // scroll to first matching element based on key pres..
39404         if (e.isSpecialKey()) {
39405             return false;
39406         }
39407         var k = String.fromCharCode(e.getKey()).toUpperCase();
39408         //Roo.log(k);
39409         var match  = false;
39410         var csel = this.view.getSelectedNodes();
39411         var cselitem = false;
39412         if (csel.length) {
39413             var ix = this.view.indexOf(csel[0]);
39414             cselitem  = this.store.getAt(ix);
39415             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
39416                 cselitem = false;
39417             }
39418             
39419         }
39420         
39421         this.store.each(function(v) { 
39422             if (cselitem) {
39423                 // start at existing selection.
39424                 if (cselitem.id == v.id) {
39425                     cselitem = false;
39426                 }
39427                 return;
39428             }
39429                 
39430             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
39431                 match = this.store.indexOf(v);
39432                 return false;
39433             }
39434         }, this);
39435         
39436         if (match === false) {
39437             return true; // no more action?
39438         }
39439         // scroll to?
39440         this.view.select(match);
39441         var sn = Roo.get(this.view.getSelectedNodes()[0])
39442         sn.scrollIntoView(sn.dom.parentNode, false);
39443     }
39444
39445     /** 
39446     * @cfg {Boolean} grow 
39447     * @hide 
39448     */
39449     /** 
39450     * @cfg {Number} growMin 
39451     * @hide 
39452     */
39453     /** 
39454     * @cfg {Number} growMax 
39455     * @hide 
39456     */
39457     /**
39458      * @hide
39459      * @method autoSize
39460      */
39461 });/*
39462  * Copyright(c) 2010-2012, Roo J Solutions Limited
39463  *
39464  * Licence LGPL
39465  *
39466  */
39467
39468 /**
39469  * @class Roo.form.ComboBoxArray
39470  * @extends Roo.form.TextField
39471  * A facebook style adder... for lists of email / people / countries  etc...
39472  * pick multiple items from a combo box, and shows each one.
39473  *
39474  *  Fred [x]  Brian [x]  [Pick another |v]
39475  *
39476  *
39477  *  For this to work: it needs various extra information
39478  *    - normal combo problay has
39479  *      name, hiddenName
39480  *    + displayField, valueField
39481  *
39482  *    For our purpose...
39483  *
39484  *
39485  *   If we change from 'extends' to wrapping...
39486  *   
39487  *  
39488  *
39489  
39490  
39491  * @constructor
39492  * Create a new ComboBoxArray.
39493  * @param {Object} config Configuration options
39494  */
39495  
39496
39497 Roo.form.ComboBoxArray = function(config)
39498 {
39499     
39500     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
39501     
39502     this.items = new Roo.util.MixedCollection(false);
39503     
39504     // construct the child combo...
39505     
39506     
39507     
39508     
39509    
39510     
39511 }
39512
39513  
39514 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
39515
39516     /**
39517      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
39518      */
39519     
39520     lastData : false,
39521     
39522     // behavies liek a hiddne field
39523     inputType:      'hidden',
39524     /**
39525      * @cfg {Number} width The width of the box that displays the selected element
39526      */ 
39527     width:          300,
39528
39529     
39530     
39531     /**
39532      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
39533      */
39534     name : false,
39535     /**
39536      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
39537      */
39538     hiddenName : false,
39539     
39540     
39541     // private the array of items that are displayed..
39542     items  : false,
39543     // private - the hidden field el.
39544     hiddenEl : false,
39545     // private - the filed el..
39546     el : false,
39547     
39548     //validateValue : function() { return true; }, // all values are ok!
39549     //onAddClick: function() { },
39550     
39551     onRender : function(ct, position) 
39552     {
39553         
39554         // create the standard hidden element
39555         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
39556         
39557         
39558         // give fake names to child combo;
39559         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
39560         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
39561         
39562         this.combo = Roo.factory(this.combo, Roo.form);
39563         this.combo.onRender(ct, position);
39564         if (typeof(this.combo.width) != 'undefined') {
39565             this.combo.onResize(this.combo.width,0);
39566         }
39567         
39568         this.combo.initEvents();
39569         
39570         // assigned so form know we need to do this..
39571         this.store          = this.combo.store;
39572         this.valueField     = this.combo.valueField;
39573         this.displayField   = this.combo.displayField ;
39574         
39575         
39576         this.combo.wrap.addClass('x-cbarray-grp');
39577         
39578         var cbwrap = this.combo.wrap.createChild(
39579             {tag: 'div', cls: 'x-cbarray-cb'},
39580             this.combo.el.dom
39581         );
39582         
39583              
39584         this.hiddenEl = this.combo.wrap.createChild({
39585             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
39586         });
39587         this.el = this.combo.wrap.createChild({
39588             tag: 'input',  type:'hidden' , name: this.name, value : ''
39589         });
39590          //   this.el.dom.removeAttribute("name");
39591         
39592         
39593         this.outerWrap = this.combo.wrap;
39594         this.wrap = cbwrap;
39595         
39596         this.outerWrap.setWidth(this.width);
39597         this.outerWrap.dom.removeChild(this.el.dom);
39598         
39599         this.wrap.dom.appendChild(this.el.dom);
39600         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
39601         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
39602         
39603         this.combo.trigger.setStyle('position','relative');
39604         this.combo.trigger.setStyle('left', '0px');
39605         this.combo.trigger.setStyle('top', '2px');
39606         
39607         this.combo.el.setStyle('vertical-align', 'text-bottom');
39608         
39609         //this.trigger.setStyle('vertical-align', 'top');
39610         
39611         // this should use the code from combo really... on('add' ....)
39612         if (this.adder) {
39613             
39614         
39615             this.adder = this.outerWrap.createChild(
39616                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
39617             var _t = this;
39618             this.adder.on('click', function(e) {
39619                 _t.fireEvent('adderclick', this, e);
39620             }, _t);
39621         }
39622         //var _t = this;
39623         //this.adder.on('click', this.onAddClick, _t);
39624         
39625         
39626         this.combo.on('select', function(cb, rec, ix) {
39627             this.addItem(rec.data);
39628             
39629             cb.setValue('');
39630             cb.el.dom.value = '';
39631             //cb.lastData = rec.data;
39632             // add to list
39633             
39634         }, this);
39635         
39636         
39637     },
39638     
39639     
39640     getName: function()
39641     {
39642         // returns hidden if it's set..
39643         if (!this.rendered) {return ''};
39644         return  this.hiddenName ? this.hiddenName : this.name;
39645         
39646     },
39647     
39648     
39649     onResize: function(w, h){
39650         
39651         return;
39652         // not sure if this is needed..
39653         //this.combo.onResize(w,h);
39654         
39655         if(typeof w != 'number'){
39656             // we do not handle it!?!?
39657             return;
39658         }
39659         var tw = this.combo.trigger.getWidth();
39660         tw += this.addicon ? this.addicon.getWidth() : 0;
39661         tw += this.editicon ? this.editicon.getWidth() : 0;
39662         var x = w - tw;
39663         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
39664             
39665         this.combo.trigger.setStyle('left', '0px');
39666         
39667         if(this.list && this.listWidth === undefined){
39668             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
39669             this.list.setWidth(lw);
39670             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39671         }
39672         
39673     
39674         
39675     },
39676     
39677     addItem: function(rec)
39678     {
39679         var valueField = this.combo.valueField;
39680         var displayField = this.combo.displayField;
39681         if (this.items.indexOfKey(rec[valueField]) > -1) {
39682             //console.log("GOT " + rec.data.id);
39683             return;
39684         }
39685         
39686         var x = new Roo.form.ComboBoxArray.Item({
39687             //id : rec[this.idField],
39688             data : rec,
39689             displayField : displayField ,
39690             tipField : displayField ,
39691             cb : this
39692         });
39693         // use the 
39694         this.items.add(rec[valueField],x);
39695         // add it before the element..
39696         this.updateHiddenEl();
39697         x.render(this.outerWrap, this.wrap.dom);
39698         // add the image handler..
39699     },
39700     
39701     updateHiddenEl : function()
39702     {
39703         this.validate();
39704         if (!this.hiddenEl) {
39705             return;
39706         }
39707         var ar = [];
39708         var idField = this.combo.valueField;
39709         
39710         this.items.each(function(f) {
39711             ar.push(f.data[idField]);
39712            
39713         });
39714         this.hiddenEl.dom.value = ar.join(',');
39715         this.validate();
39716     },
39717     
39718     reset : function()
39719     {
39720         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
39721         this.items.each(function(f) {
39722            f.remove(); 
39723         });
39724         this.el.dom.value = '';
39725         if (this.hiddenEl) {
39726             this.hiddenEl.dom.value = '';
39727         }
39728         
39729     },
39730     getValue: function()
39731     {
39732         return this.hiddenEl ? this.hiddenEl.dom.value : '';
39733     },
39734     setValue: function(v) // not a valid action - must use addItems..
39735     {
39736          
39737         this.reset();
39738         
39739         
39740         
39741         if (this.store.isLocal && (typeof(v) == 'string')) {
39742             // then we can use the store to find the values..
39743             // comma seperated at present.. this needs to allow JSON based encoding..
39744             this.hiddenEl.value  = v;
39745             var v_ar = [];
39746             Roo.each(v.split(','), function(k) {
39747                 Roo.log("CHECK " + this.valueField + ',' + k);
39748                 var li = this.store.query(this.valueField, k);
39749                 if (!li.length) {
39750                     return;
39751                 }
39752                 var add = {};
39753                 add[this.valueField] = k;
39754                 add[this.displayField] = li.item(0).data[this.displayField];
39755                 
39756                 this.addItem(add);
39757             }, this) 
39758              
39759         }
39760         if (typeof(v) == 'object') {
39761             // then let's assume it's an array of objects..
39762             Roo.each(v, function(l) {
39763                 this.addItem(l);
39764             }, this);
39765              
39766         }
39767         
39768         
39769     },
39770     setFromData: function(v)
39771     {
39772         // this recieves an object, if setValues is called.
39773         this.reset();
39774         this.el.dom.value = v[this.displayField];
39775         this.hiddenEl.dom.value = v[this.valueField];
39776         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
39777             return;
39778         }
39779         var kv = v[this.valueField];
39780         var dv = v[this.displayField];
39781         kv = typeof(kv) != 'string' ? '' : kv;
39782         dv = typeof(dv) != 'string' ? '' : dv;
39783         
39784         
39785         var keys = kv.split(',');
39786         var display = dv.split(',');
39787         for (var i = 0 ; i < keys.length; i++) {
39788             
39789             add = {};
39790             add[this.valueField] = keys[i];
39791             add[this.displayField] = display[i];
39792             this.addItem(add);
39793         }
39794       
39795         
39796     },
39797     
39798     
39799     validateValue : function(value){
39800         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
39801         
39802     }
39803     
39804 });
39805
39806
39807
39808 /**
39809  * @class Roo.form.ComboBoxArray.Item
39810  * @extends Roo.BoxComponent
39811  * A selected item in the list
39812  *  Fred [x]  Brian [x]  [Pick another |v]
39813  * 
39814  * @constructor
39815  * Create a new item.
39816  * @param {Object} config Configuration options
39817  */
39818  
39819 Roo.form.ComboBoxArray.Item = function(config) {
39820     config.id = Roo.id();
39821     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
39822 }
39823
39824 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
39825     data : {},
39826     cb: false,
39827     displayField : false,
39828     tipField : false,
39829     
39830     
39831     defaultAutoCreate : {
39832         tag: 'div',
39833         cls: 'x-cbarray-item',
39834         cn : [ 
39835             { tag: 'div' },
39836             {
39837                 tag: 'img',
39838                 width:16,
39839                 height : 16,
39840                 src : Roo.BLANK_IMAGE_URL ,
39841                 align: 'center'
39842             }
39843         ]
39844         
39845     },
39846     
39847  
39848     onRender : function(ct, position)
39849     {
39850         Roo.form.Field.superclass.onRender.call(this, ct, position);
39851         
39852         if(!this.el){
39853             var cfg = this.getAutoCreate();
39854             this.el = ct.createChild(cfg, position);
39855         }
39856         
39857         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
39858         
39859         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
39860             this.cb.renderer(this.data) :
39861             String.format('{0}',this.data[this.displayField]);
39862         
39863             
39864         this.el.child('div').dom.setAttribute('qtip',
39865                         String.format('{0}',this.data[this.tipField])
39866         );
39867         
39868         this.el.child('img').on('click', this.remove, this);
39869         
39870     },
39871    
39872     remove : function()
39873     {
39874         
39875         this.cb.items.remove(this);
39876         this.el.child('img').un('click', this.remove, this);
39877         this.el.remove();
39878         this.cb.updateHiddenEl();
39879     }
39880     
39881     
39882 });/*
39883  * Based on:
39884  * Ext JS Library 1.1.1
39885  * Copyright(c) 2006-2007, Ext JS, LLC.
39886  *
39887  * Originally Released Under LGPL - original licence link has changed is not relivant.
39888  *
39889  * Fork - LGPL
39890  * <script type="text/javascript">
39891  */
39892 /**
39893  * @class Roo.form.Checkbox
39894  * @extends Roo.form.Field
39895  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
39896  * @constructor
39897  * Creates a new Checkbox
39898  * @param {Object} config Configuration options
39899  */
39900 Roo.form.Checkbox = function(config){
39901     Roo.form.Checkbox.superclass.constructor.call(this, config);
39902     this.addEvents({
39903         /**
39904          * @event check
39905          * Fires when the checkbox is checked or unchecked.
39906              * @param {Roo.form.Checkbox} this This checkbox
39907              * @param {Boolean} checked The new checked value
39908              */
39909         check : true
39910     });
39911 };
39912
39913 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
39914     /**
39915      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
39916      */
39917     focusClass : undefined,
39918     /**
39919      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
39920      */
39921     fieldClass: "x-form-field",
39922     /**
39923      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
39924      */
39925     checked: false,
39926     /**
39927      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39928      * {tag: "input", type: "checkbox", autocomplete: "off"})
39929      */
39930     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
39931     /**
39932      * @cfg {String} boxLabel The text that appears beside the checkbox
39933      */
39934     boxLabel : "",
39935     /**
39936      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
39937      */  
39938     inputValue : '1',
39939     /**
39940      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
39941      */
39942      valueOff: '0', // value when not checked..
39943
39944     actionMode : 'viewEl', 
39945     //
39946     // private
39947     itemCls : 'x-menu-check-item x-form-item',
39948     groupClass : 'x-menu-group-item',
39949     inputType : 'hidden',
39950     
39951     
39952     inSetChecked: false, // check that we are not calling self...
39953     
39954     inputElement: false, // real input element?
39955     basedOn: false, // ????
39956     
39957     isFormField: true, // not sure where this is needed!!!!
39958
39959     onResize : function(){
39960         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
39961         if(!this.boxLabel){
39962             this.el.alignTo(this.wrap, 'c-c');
39963         }
39964     },
39965
39966     initEvents : function(){
39967         Roo.form.Checkbox.superclass.initEvents.call(this);
39968         this.el.on("click", this.onClick,  this);
39969         this.el.on("change", this.onClick,  this);
39970     },
39971
39972
39973     getResizeEl : function(){
39974         return this.wrap;
39975     },
39976
39977     getPositionEl : function(){
39978         return this.wrap;
39979     },
39980
39981     // private
39982     onRender : function(ct, position){
39983         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
39984         /*
39985         if(this.inputValue !== undefined){
39986             this.el.dom.value = this.inputValue;
39987         }
39988         */
39989         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
39990         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
39991         var viewEl = this.wrap.createChild({ 
39992             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
39993         this.viewEl = viewEl;   
39994         this.wrap.on('click', this.onClick,  this); 
39995         
39996         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
39997         this.el.on('propertychange', this.setFromHidden,  this);  //ie
39998         
39999         
40000         
40001         if(this.boxLabel){
40002             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40003         //    viewEl.on('click', this.onClick,  this); 
40004         }
40005         //if(this.checked){
40006             this.setChecked(this.checked);
40007         //}else{
40008             //this.checked = this.el.dom;
40009         //}
40010
40011     },
40012
40013     // private
40014     initValue : Roo.emptyFn,
40015
40016     /**
40017      * Returns the checked state of the checkbox.
40018      * @return {Boolean} True if checked, else false
40019      */
40020     getValue : function(){
40021         if(this.el){
40022             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40023         }
40024         return this.valueOff;
40025         
40026     },
40027
40028         // private
40029     onClick : function(){ 
40030         this.setChecked(!this.checked);
40031
40032         //if(this.el.dom.checked != this.checked){
40033         //    this.setValue(this.el.dom.checked);
40034        // }
40035     },
40036
40037     /**
40038      * Sets the checked state of the checkbox.
40039      * On is always based on a string comparison between inputValue and the param.
40040      * @param {Boolean/String} value - the value to set 
40041      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40042      */
40043     setValue : function(v,suppressEvent){
40044         
40045         
40046         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40047         //if(this.el && this.el.dom){
40048         //    this.el.dom.checked = this.checked;
40049         //    this.el.dom.defaultChecked = this.checked;
40050         //}
40051         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40052         //this.fireEvent("check", this, this.checked);
40053     },
40054     // private..
40055     setChecked : function(state,suppressEvent)
40056     {
40057         if (this.inSetChecked) {
40058             this.checked = state;
40059             return;
40060         }
40061         
40062     
40063         if(this.wrap){
40064             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40065         }
40066         this.checked = state;
40067         if(suppressEvent !== true){
40068             this.fireEvent('check', this, state);
40069         }
40070         this.inSetChecked = true;
40071         this.el.dom.value = state ? this.inputValue : this.valueOff;
40072         this.inSetChecked = false;
40073         
40074     },
40075     // handle setting of hidden value by some other method!!?!?
40076     setFromHidden: function()
40077     {
40078         if(!this.el){
40079             return;
40080         }
40081         //console.log("SET FROM HIDDEN");
40082         //alert('setFrom hidden');
40083         this.setValue(this.el.dom.value);
40084     },
40085     
40086     onDestroy : function()
40087     {
40088         if(this.viewEl){
40089             Roo.get(this.viewEl).remove();
40090         }
40091          
40092         Roo.form.Checkbox.superclass.onDestroy.call(this);
40093     }
40094
40095 });/*
40096  * Based on:
40097  * Ext JS Library 1.1.1
40098  * Copyright(c) 2006-2007, Ext JS, LLC.
40099  *
40100  * Originally Released Under LGPL - original licence link has changed is not relivant.
40101  *
40102  * Fork - LGPL
40103  * <script type="text/javascript">
40104  */
40105  
40106 /**
40107  * @class Roo.form.Radio
40108  * @extends Roo.form.Checkbox
40109  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40110  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40111  * @constructor
40112  * Creates a new Radio
40113  * @param {Object} config Configuration options
40114  */
40115 Roo.form.Radio = function(){
40116     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40117 };
40118 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40119     inputType: 'radio',
40120
40121     /**
40122      * If this radio is part of a group, it will return the selected value
40123      * @return {String}
40124      */
40125     getGroupValue : function(){
40126         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40127     },
40128     
40129     
40130     onRender : function(ct, position){
40131         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40132         
40133         if(this.inputValue !== undefined){
40134             this.el.dom.value = this.inputValue;
40135         }
40136          
40137         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40138         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40139         //var viewEl = this.wrap.createChild({ 
40140         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40141         //this.viewEl = viewEl;   
40142         //this.wrap.on('click', this.onClick,  this); 
40143         
40144         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40145         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40146         
40147         
40148         
40149         if(this.boxLabel){
40150             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40151         //    viewEl.on('click', this.onClick,  this); 
40152         }
40153          if(this.checked){
40154             this.el.dom.checked =   'checked' ;
40155         }
40156          
40157     } 
40158     
40159     
40160 });//<script type="text/javascript">
40161
40162 /*
40163  * Ext JS Library 1.1.1
40164  * Copyright(c) 2006-2007, Ext JS, LLC.
40165  * licensing@extjs.com
40166  * 
40167  * http://www.extjs.com/license
40168  */
40169  
40170  /*
40171   * 
40172   * Known bugs:
40173   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
40174   * - IE ? - no idea how much works there.
40175   * 
40176   * 
40177   * 
40178   */
40179  
40180
40181 /**
40182  * @class Ext.form.HtmlEditor
40183  * @extends Ext.form.Field
40184  * Provides a lightweight HTML Editor component.
40185  *
40186  * This has been tested on Fireforx / Chrome.. IE may not be so great..
40187  * 
40188  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
40189  * supported by this editor.</b><br/><br/>
40190  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
40191  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40192  */
40193 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
40194       /**
40195      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
40196      */
40197     toolbars : false,
40198     /**
40199      * @cfg {String} createLinkText The default text for the create link prompt
40200      */
40201     createLinkText : 'Please enter the URL for the link:',
40202     /**
40203      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
40204      */
40205     defaultLinkValue : 'http:/'+'/',
40206    
40207      /**
40208      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40209      *                        Roo.resizable.
40210      */
40211     resizable : false,
40212      /**
40213      * @cfg {Number} height (in pixels)
40214      */   
40215     height: 300,
40216    /**
40217      * @cfg {Number} width (in pixels)
40218      */   
40219     width: 500,
40220     
40221     /**
40222      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40223      * 
40224      */
40225     stylesheets: false,
40226     
40227     // id of frame..
40228     frameId: false,
40229     
40230     // private properties
40231     validationEvent : false,
40232     deferHeight: true,
40233     initialized : false,
40234     activated : false,
40235     sourceEditMode : false,
40236     onFocus : Roo.emptyFn,
40237     iframePad:3,
40238     hideMode:'offsets',
40239     
40240     defaultAutoCreate : { // modified by initCompnoent..
40241         tag: "textarea",
40242         style:"width:500px;height:300px;",
40243         autocomplete: "off"
40244     },
40245
40246     // private
40247     initComponent : function(){
40248         this.addEvents({
40249             /**
40250              * @event initialize
40251              * Fires when the editor is fully initialized (including the iframe)
40252              * @param {HtmlEditor} this
40253              */
40254             initialize: true,
40255             /**
40256              * @event activate
40257              * Fires when the editor is first receives the focus. Any insertion must wait
40258              * until after this event.
40259              * @param {HtmlEditor} this
40260              */
40261             activate: true,
40262              /**
40263              * @event beforesync
40264              * Fires before the textarea is updated with content from the editor iframe. Return false
40265              * to cancel the sync.
40266              * @param {HtmlEditor} this
40267              * @param {String} html
40268              */
40269             beforesync: true,
40270              /**
40271              * @event beforepush
40272              * Fires before the iframe editor is updated with content from the textarea. Return false
40273              * to cancel the push.
40274              * @param {HtmlEditor} this
40275              * @param {String} html
40276              */
40277             beforepush: true,
40278              /**
40279              * @event sync
40280              * Fires when the textarea is updated with content from the editor iframe.
40281              * @param {HtmlEditor} this
40282              * @param {String} html
40283              */
40284             sync: true,
40285              /**
40286              * @event push
40287              * Fires when the iframe editor is updated with content from the textarea.
40288              * @param {HtmlEditor} this
40289              * @param {String} html
40290              */
40291             push: true,
40292              /**
40293              * @event editmodechange
40294              * Fires when the editor switches edit modes
40295              * @param {HtmlEditor} this
40296              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
40297              */
40298             editmodechange: true,
40299             /**
40300              * @event editorevent
40301              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40302              * @param {HtmlEditor} this
40303              */
40304             editorevent: true
40305         });
40306         this.defaultAutoCreate =  {
40307             tag: "textarea",
40308             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
40309             autocomplete: "off"
40310         };
40311     },
40312
40313     /**
40314      * Protected method that will not generally be called directly. It
40315      * is called when the editor creates its toolbar. Override this method if you need to
40316      * add custom toolbar buttons.
40317      * @param {HtmlEditor} editor
40318      */
40319     createToolbar : function(editor){
40320         if (!editor.toolbars || !editor.toolbars.length) {
40321             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
40322         }
40323         
40324         for (var i =0 ; i < editor.toolbars.length;i++) {
40325             editor.toolbars[i] = Roo.factory(
40326                     typeof(editor.toolbars[i]) == 'string' ?
40327                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
40328                 Roo.form.HtmlEditor);
40329             editor.toolbars[i].init(editor);
40330         }
40331          
40332         
40333     },
40334
40335     /**
40336      * Protected method that will not generally be called directly. It
40337      * is called when the editor initializes the iframe with HTML contents. Override this method if you
40338      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
40339      */
40340     getDocMarkup : function(){
40341         // body styles..
40342         var st = '';
40343         if (this.stylesheets === false) {
40344             
40345             Roo.get(document.head).select('style').each(function(node) {
40346                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40347             });
40348             
40349             Roo.get(document.head).select('link').each(function(node) { 
40350                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40351             });
40352             
40353         } else if (!this.stylesheets.length) {
40354                 // simple..
40355                 st = '<style type="text/css">' +
40356                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40357                    '</style>';
40358         } else {
40359             Roo.each(this.stylesheets, function(s) {
40360                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
40361             });
40362             
40363         }
40364         
40365         st +=  '<style type="text/css">' +
40366             'IMG { cursor: pointer } ' +
40367         '</style>';
40368
40369         
40370         return '<html><head>' + st  +
40371             //<style type="text/css">' +
40372             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40373             //'</style>' +
40374             ' </head><body class="roo-htmleditor-body"></body></html>';
40375     },
40376
40377     // private
40378     onRender : function(ct, position)
40379     {
40380         var _t = this;
40381         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
40382         this.el.dom.style.border = '0 none';
40383         this.el.dom.setAttribute('tabIndex', -1);
40384         this.el.addClass('x-hidden');
40385         if(Roo.isIE){ // fix IE 1px bogus margin
40386             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
40387         }
40388         this.wrap = this.el.wrap({
40389             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
40390         });
40391         
40392         if (this.resizable) {
40393             this.resizeEl = new Roo.Resizable(this.wrap, {
40394                 pinned : true,
40395                 wrap: true,
40396                 dynamic : true,
40397                 minHeight : this.height,
40398                 height: this.height,
40399                 handles : this.resizable,
40400                 width: this.width,
40401                 listeners : {
40402                     resize : function(r, w, h) {
40403                         _t.onResize(w,h); // -something
40404                     }
40405                 }
40406             });
40407             
40408         }
40409
40410         this.frameId = Roo.id();
40411         
40412         this.createToolbar(this);
40413         
40414       
40415         
40416         var iframe = this.wrap.createChild({
40417             tag: 'iframe',
40418             id: this.frameId,
40419             name: this.frameId,
40420             frameBorder : 'no',
40421             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
40422         }, this.el
40423         );
40424         
40425        // console.log(iframe);
40426         //this.wrap.dom.appendChild(iframe);
40427
40428         this.iframe = iframe.dom;
40429
40430          this.assignDocWin();
40431         
40432         this.doc.designMode = 'on';
40433        
40434         this.doc.open();
40435         this.doc.write(this.getDocMarkup());
40436         this.doc.close();
40437
40438         
40439         var task = { // must defer to wait for browser to be ready
40440             run : function(){
40441                 //console.log("run task?" + this.doc.readyState);
40442                 this.assignDocWin();
40443                 if(this.doc.body || this.doc.readyState == 'complete'){
40444                     try {
40445                         this.doc.designMode="on";
40446                     } catch (e) {
40447                         return;
40448                     }
40449                     Roo.TaskMgr.stop(task);
40450                     this.initEditor.defer(10, this);
40451                 }
40452             },
40453             interval : 10,
40454             duration:10000,
40455             scope: this
40456         };
40457         Roo.TaskMgr.start(task);
40458
40459         if(!this.width){
40460             this.setSize(this.wrap.getSize());
40461         }
40462         if (this.resizeEl) {
40463             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
40464             // should trigger onReize..
40465         }
40466     },
40467
40468     // private
40469     onResize : function(w, h)
40470     {
40471         //Roo.log('resize: ' +w + ',' + h );
40472         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
40473         if(this.el && this.iframe){
40474             if(typeof w == 'number'){
40475                 var aw = w - this.wrap.getFrameWidth('lr');
40476                 this.el.setWidth(this.adjustWidth('textarea', aw));
40477                 this.iframe.style.width = aw + 'px';
40478             }
40479             if(typeof h == 'number'){
40480                 var tbh = 0;
40481                 for (var i =0; i < this.toolbars.length;i++) {
40482                     // fixme - ask toolbars for heights?
40483                     tbh += this.toolbars[i].tb.el.getHeight();
40484                     if (this.toolbars[i].footer) {
40485                         tbh += this.toolbars[i].footer.el.getHeight();
40486                     }
40487                 }
40488                 
40489                 
40490                 
40491                 
40492                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
40493                 ah -= 5; // knock a few pixes off for look..
40494                 this.el.setHeight(this.adjustWidth('textarea', ah));
40495                 this.iframe.style.height = ah + 'px';
40496                 if(this.doc){
40497                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
40498                 }
40499             }
40500         }
40501     },
40502
40503     /**
40504      * Toggles the editor between standard and source edit mode.
40505      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
40506      */
40507     toggleSourceEdit : function(sourceEditMode){
40508         
40509         this.sourceEditMode = sourceEditMode === true;
40510         
40511         if(this.sourceEditMode){
40512 //            Roo.log('in');
40513 //            Roo.log(this.syncValue());
40514             this.syncValue();
40515             this.iframe.className = 'x-hidden';
40516             this.el.removeClass('x-hidden');
40517             this.el.dom.removeAttribute('tabIndex');
40518             this.el.focus();
40519         }else{
40520 //            Roo.log('out')
40521 //            Roo.log(this.pushValue()); 
40522             this.pushValue();
40523             this.iframe.className = '';
40524             this.el.addClass('x-hidden');
40525             this.el.dom.setAttribute('tabIndex', -1);
40526             this.deferFocus();
40527         }
40528         this.setSize(this.wrap.getSize());
40529         this.fireEvent('editmodechange', this, this.sourceEditMode);
40530     },
40531
40532     // private used internally
40533     createLink : function(){
40534         var url = prompt(this.createLinkText, this.defaultLinkValue);
40535         if(url && url != 'http:/'+'/'){
40536             this.relayCmd('createlink', url);
40537         }
40538     },
40539
40540     // private (for BoxComponent)
40541     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40542
40543     // private (for BoxComponent)
40544     getResizeEl : function(){
40545         return this.wrap;
40546     },
40547
40548     // private (for BoxComponent)
40549     getPositionEl : function(){
40550         return this.wrap;
40551     },
40552
40553     // private
40554     initEvents : function(){
40555         this.originalValue = this.getValue();
40556     },
40557
40558     /**
40559      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40560      * @method
40561      */
40562     markInvalid : Roo.emptyFn,
40563     /**
40564      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40565      * @method
40566      */
40567     clearInvalid : Roo.emptyFn,
40568
40569     setValue : function(v){
40570         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
40571         this.pushValue();
40572     },
40573
40574     /**
40575      * Protected method that will not generally be called directly. If you need/want
40576      * custom HTML cleanup, this is the method you should override.
40577      * @param {String} html The HTML to be cleaned
40578      * return {String} The cleaned HTML
40579      */
40580     cleanHtml : function(html){
40581         html = String(html);
40582         if(html.length > 5){
40583             if(Roo.isSafari){ // strip safari nonsense
40584                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
40585             }
40586         }
40587         if(html == '&nbsp;'){
40588             html = '';
40589         }
40590         return html;
40591     },
40592
40593     /**
40594      * Protected method that will not generally be called directly. Syncs the contents
40595      * of the editor iframe with the textarea.
40596      */
40597     syncValue : function(){
40598         if(this.initialized){
40599             var bd = (this.doc.body || this.doc.documentElement);
40600             //this.cleanUpPaste(); -- this is done else where and causes havoc..
40601             var html = bd.innerHTML;
40602             if(Roo.isSafari){
40603                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
40604                 var m = bs.match(/text-align:(.*?);/i);
40605                 if(m && m[1]){
40606                     html = '<div style="'+m[0]+'">' + html + '</div>';
40607                 }
40608             }
40609             html = this.cleanHtml(html);
40610             // fix up the special chars.. normaly like back quotes in word...
40611             // however we do not want to do this with chinese..
40612             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
40613                 var cc = b.charCodeAt();
40614                 if (
40615                     (cc >= 0x4E00 && cc < 0xA000 ) ||
40616                     (cc >= 0x3400 && cc < 0x4E00 ) ||
40617                     (cc >= 0xf900 && cc < 0xfb00 )
40618                 ) {
40619                         return b;
40620                 }
40621                 return "&#"+cc+";" 
40622             });
40623             if(this.fireEvent('beforesync', this, html) !== false){
40624                 this.el.dom.value = html;
40625                 this.fireEvent('sync', this, html);
40626             }
40627         }
40628     },
40629
40630     /**
40631      * Protected method that will not generally be called directly. Pushes the value of the textarea
40632      * into the iframe editor.
40633      */
40634     pushValue : function(){
40635         if(this.initialized){
40636             var v = this.el.dom.value;
40637             
40638             if(v.length < 1){
40639                 v = '&#160;';
40640             }
40641             
40642             if(this.fireEvent('beforepush', this, v) !== false){
40643                 var d = (this.doc.body || this.doc.documentElement);
40644                 d.innerHTML = v;
40645                 this.cleanUpPaste();
40646                 this.el.dom.value = d.innerHTML;
40647                 this.fireEvent('push', this, v);
40648             }
40649         }
40650     },
40651
40652     // private
40653     deferFocus : function(){
40654         this.focus.defer(10, this);
40655     },
40656
40657     // doc'ed in Field
40658     focus : function(){
40659         if(this.win && !this.sourceEditMode){
40660             this.win.focus();
40661         }else{
40662             this.el.focus();
40663         }
40664     },
40665     
40666     assignDocWin: function()
40667     {
40668         var iframe = this.iframe;
40669         
40670          if(Roo.isIE){
40671             this.doc = iframe.contentWindow.document;
40672             this.win = iframe.contentWindow;
40673         } else {
40674             if (!Roo.get(this.frameId)) {
40675                 return;
40676             }
40677             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
40678             this.win = Roo.get(this.frameId).dom.contentWindow;
40679         }
40680     },
40681     
40682     // private
40683     initEditor : function(){
40684         //console.log("INIT EDITOR");
40685         this.assignDocWin();
40686         
40687         
40688         
40689         this.doc.designMode="on";
40690         this.doc.open();
40691         this.doc.write(this.getDocMarkup());
40692         this.doc.close();
40693         
40694         var dbody = (this.doc.body || this.doc.documentElement);
40695         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
40696         // this copies styles from the containing element into thsi one..
40697         // not sure why we need all of this..
40698         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
40699         ss['background-attachment'] = 'fixed'; // w3c
40700         dbody.bgProperties = 'fixed'; // ie
40701         Roo.DomHelper.applyStyles(dbody, ss);
40702         Roo.EventManager.on(this.doc, {
40703             //'mousedown': this.onEditorEvent,
40704             'mouseup': this.onEditorEvent,
40705             'dblclick': this.onEditorEvent,
40706             'click': this.onEditorEvent,
40707             'keyup': this.onEditorEvent,
40708             buffer:100,
40709             scope: this
40710         });
40711         if(Roo.isGecko){
40712             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
40713         }
40714         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
40715             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
40716         }
40717         this.initialized = true;
40718
40719         this.fireEvent('initialize', this);
40720         this.pushValue();
40721     },
40722
40723     // private
40724     onDestroy : function(){
40725         
40726         
40727         
40728         if(this.rendered){
40729             
40730             for (var i =0; i < this.toolbars.length;i++) {
40731                 // fixme - ask toolbars for heights?
40732                 this.toolbars[i].onDestroy();
40733             }
40734             
40735             this.wrap.dom.innerHTML = '';
40736             this.wrap.remove();
40737         }
40738     },
40739
40740     // private
40741     onFirstFocus : function(){
40742         
40743         this.assignDocWin();
40744         
40745         
40746         this.activated = true;
40747         for (var i =0; i < this.toolbars.length;i++) {
40748             this.toolbars[i].onFirstFocus();
40749         }
40750        
40751         if(Roo.isGecko){ // prevent silly gecko errors
40752             this.win.focus();
40753             var s = this.win.getSelection();
40754             if(!s.focusNode || s.focusNode.nodeType != 3){
40755                 var r = s.getRangeAt(0);
40756                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
40757                 r.collapse(true);
40758                 this.deferFocus();
40759             }
40760             try{
40761                 this.execCmd('useCSS', true);
40762                 this.execCmd('styleWithCSS', false);
40763             }catch(e){}
40764         }
40765         this.fireEvent('activate', this);
40766     },
40767
40768     // private
40769     adjustFont: function(btn){
40770         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
40771         //if(Roo.isSafari){ // safari
40772         //    adjust *= 2;
40773        // }
40774         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
40775         if(Roo.isSafari){ // safari
40776             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
40777             v =  (v < 10) ? 10 : v;
40778             v =  (v > 48) ? 48 : v;
40779             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
40780             
40781         }
40782         
40783         
40784         v = Math.max(1, v+adjust);
40785         
40786         this.execCmd('FontSize', v  );
40787     },
40788
40789     onEditorEvent : function(e){
40790         this.fireEvent('editorevent', this, e);
40791       //  this.updateToolbar();
40792         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
40793     },
40794
40795     insertTag : function(tg)
40796     {
40797         // could be a bit smarter... -> wrap the current selected tRoo..
40798         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
40799             
40800             range = this.createRange(this.getSelection());
40801             var wrappingNode = this.doc.createElement(tg.toLowerCase());
40802             wrappingNode.appendChild(range.extractContents());
40803             range.insertNode(wrappingNode);
40804
40805             return;
40806             
40807             
40808             
40809         }
40810         this.execCmd("formatblock",   tg);
40811         
40812     },
40813     
40814     insertText : function(txt)
40815     {
40816         
40817         
40818         var range = this.createRange();
40819         range.deleteContents();
40820                //alert(Sender.getAttribute('label'));
40821                
40822         range.insertNode(this.doc.createTextNode(txt));
40823     } ,
40824     
40825     // private
40826     relayBtnCmd : function(btn){
40827         this.relayCmd(btn.cmd);
40828     },
40829
40830     /**
40831      * Executes a Midas editor command on the editor document and performs necessary focus and
40832      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
40833      * @param {String} cmd The Midas command
40834      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40835      */
40836     relayCmd : function(cmd, value){
40837         this.win.focus();
40838         this.execCmd(cmd, value);
40839         this.fireEvent('editorevent', this);
40840         //this.updateToolbar();
40841         this.deferFocus();
40842     },
40843
40844     /**
40845      * Executes a Midas editor command directly on the editor document.
40846      * For visual commands, you should use {@link #relayCmd} instead.
40847      * <b>This should only be called after the editor is initialized.</b>
40848      * @param {String} cmd The Midas command
40849      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40850      */
40851     execCmd : function(cmd, value){
40852         this.doc.execCommand(cmd, false, value === undefined ? null : value);
40853         this.syncValue();
40854     },
40855  
40856  
40857    
40858     /**
40859      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
40860      * to insert tRoo.
40861      * @param {String} text | dom node.. 
40862      */
40863     insertAtCursor : function(text)
40864     {
40865         
40866         
40867         
40868         if(!this.activated){
40869             return;
40870         }
40871         /*
40872         if(Roo.isIE){
40873             this.win.focus();
40874             var r = this.doc.selection.createRange();
40875             if(r){
40876                 r.collapse(true);
40877                 r.pasteHTML(text);
40878                 this.syncValue();
40879                 this.deferFocus();
40880             
40881             }
40882             return;
40883         }
40884         */
40885         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
40886             this.win.focus();
40887             
40888             
40889             // from jquery ui (MIT licenced)
40890             var range, node;
40891             var win = this.win;
40892             
40893             if (win.getSelection && win.getSelection().getRangeAt) {
40894                 range = win.getSelection().getRangeAt(0);
40895                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
40896                 range.insertNode(node);
40897             } else if (win.document.selection && win.document.selection.createRange) {
40898                 // no firefox support
40899                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
40900                 win.document.selection.createRange().pasteHTML(txt);
40901             } else {
40902                 // no firefox support
40903                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
40904                 this.execCmd('InsertHTML', txt);
40905             } 
40906             
40907             this.syncValue();
40908             
40909             this.deferFocus();
40910         }
40911     },
40912  // private
40913     mozKeyPress : function(e){
40914         if(e.ctrlKey){
40915             var c = e.getCharCode(), cmd;
40916           
40917             if(c > 0){
40918                 c = String.fromCharCode(c).toLowerCase();
40919                 switch(c){
40920                     case 'b':
40921                         cmd = 'bold';
40922                         break;
40923                     case 'i':
40924                         cmd = 'italic';
40925                         break;
40926                     
40927                     case 'u':
40928                         cmd = 'underline';
40929                         break;
40930                     
40931                     case 'v':
40932                         this.cleanUpPaste.defer(100, this);
40933                         return;
40934                         
40935                 }
40936                 if(cmd){
40937                     this.win.focus();
40938                     this.execCmd(cmd);
40939                     this.deferFocus();
40940                     e.preventDefault();
40941                 }
40942                 
40943             }
40944         }
40945     },
40946
40947     // private
40948     fixKeys : function(){ // load time branching for fastest keydown performance
40949         if(Roo.isIE){
40950             return function(e){
40951                 var k = e.getKey(), r;
40952                 if(k == e.TAB){
40953                     e.stopEvent();
40954                     r = this.doc.selection.createRange();
40955                     if(r){
40956                         r.collapse(true);
40957                         r.pasteHTML('&#160;&#160;&#160;&#160;');
40958                         this.deferFocus();
40959                     }
40960                     return;
40961                 }
40962                 
40963                 if(k == e.ENTER){
40964                     r = this.doc.selection.createRange();
40965                     if(r){
40966                         var target = r.parentElement();
40967                         if(!target || target.tagName.toLowerCase() != 'li'){
40968                             e.stopEvent();
40969                             r.pasteHTML('<br />');
40970                             r.collapse(false);
40971                             r.select();
40972                         }
40973                     }
40974                 }
40975                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40976                     this.cleanUpPaste.defer(100, this);
40977                     return;
40978                 }
40979                 
40980                 
40981             };
40982         }else if(Roo.isOpera){
40983             return function(e){
40984                 var k = e.getKey();
40985                 if(k == e.TAB){
40986                     e.stopEvent();
40987                     this.win.focus();
40988                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
40989                     this.deferFocus();
40990                 }
40991                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40992                     this.cleanUpPaste.defer(100, this);
40993                     return;
40994                 }
40995                 
40996             };
40997         }else if(Roo.isSafari){
40998             return function(e){
40999                 var k = e.getKey();
41000                 
41001                 if(k == e.TAB){
41002                     e.stopEvent();
41003                     this.execCmd('InsertText','\t');
41004                     this.deferFocus();
41005                     return;
41006                 }
41007                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41008                     this.cleanUpPaste.defer(100, this);
41009                     return;
41010                 }
41011                 
41012              };
41013         }
41014     }(),
41015     
41016     getAllAncestors: function()
41017     {
41018         var p = this.getSelectedNode();
41019         var a = [];
41020         if (!p) {
41021             a.push(p); // push blank onto stack..
41022             p = this.getParentElement();
41023         }
41024         
41025         
41026         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41027             a.push(p);
41028             p = p.parentNode;
41029         }
41030         a.push(this.doc.body);
41031         return a;
41032     },
41033     lastSel : false,
41034     lastSelNode : false,
41035     
41036     
41037     getSelection : function() 
41038     {
41039         this.assignDocWin();
41040         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41041     },
41042     
41043     getSelectedNode: function() 
41044     {
41045         // this may only work on Gecko!!!
41046         
41047         // should we cache this!!!!
41048         
41049         
41050         
41051          
41052         var range = this.createRange(this.getSelection()).cloneRange();
41053         
41054         if (Roo.isIE) {
41055             var parent = range.parentElement();
41056             while (true) {
41057                 var testRange = range.duplicate();
41058                 testRange.moveToElementText(parent);
41059                 if (testRange.inRange(range)) {
41060                     break;
41061                 }
41062                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41063                     break;
41064                 }
41065                 parent = parent.parentElement;
41066             }
41067             return parent;
41068         }
41069         
41070         // is ancestor a text element.
41071         var ac =  range.commonAncestorContainer;
41072         if (ac.nodeType == 3) {
41073             ac = ac.parentNode;
41074         }
41075         
41076         var ar = ac.childNodes;
41077          
41078         var nodes = [];
41079         var other_nodes = [];
41080         var has_other_nodes = false;
41081         for (var i=0;i<ar.length;i++) {
41082             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41083                 continue;
41084             }
41085             // fullly contained node.
41086             
41087             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41088                 nodes.push(ar[i]);
41089                 continue;
41090             }
41091             
41092             // probably selected..
41093             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41094                 other_nodes.push(ar[i]);
41095                 continue;
41096             }
41097             // outer..
41098             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41099                 continue;
41100             }
41101             
41102             
41103             has_other_nodes = true;
41104         }
41105         if (!nodes.length && other_nodes.length) {
41106             nodes= other_nodes;
41107         }
41108         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41109             return false;
41110         }
41111         
41112         return nodes[0];
41113     },
41114     createRange: function(sel)
41115     {
41116         // this has strange effects when using with 
41117         // top toolbar - not sure if it's a great idea.
41118         //this.editor.contentWindow.focus();
41119         if (typeof sel != "undefined") {
41120             try {
41121                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41122             } catch(e) {
41123                 return this.doc.createRange();
41124             }
41125         } else {
41126             return this.doc.createRange();
41127         }
41128     },
41129     getParentElement: function()
41130     {
41131         
41132         this.assignDocWin();
41133         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41134         
41135         var range = this.createRange(sel);
41136          
41137         try {
41138             var p = range.commonAncestorContainer;
41139             while (p.nodeType == 3) { // text node
41140                 p = p.parentNode;
41141             }
41142             return p;
41143         } catch (e) {
41144             return null;
41145         }
41146     
41147     },
41148     /***
41149      *
41150      * Range intersection.. the hard stuff...
41151      *  '-1' = before
41152      *  '0' = hits..
41153      *  '1' = after.
41154      *         [ -- selected range --- ]
41155      *   [fail]                        [fail]
41156      *
41157      *    basically..
41158      *      if end is before start or  hits it. fail.
41159      *      if start is after end or hits it fail.
41160      *
41161      *   if either hits (but other is outside. - then it's not 
41162      *   
41163      *    
41164      **/
41165     
41166     
41167     // @see http://www.thismuchiknow.co.uk/?p=64.
41168     rangeIntersectsNode : function(range, node)
41169     {
41170         var nodeRange = node.ownerDocument.createRange();
41171         try {
41172             nodeRange.selectNode(node);
41173         } catch (e) {
41174             nodeRange.selectNodeContents(node);
41175         }
41176     
41177         var rangeStartRange = range.cloneRange();
41178         rangeStartRange.collapse(true);
41179     
41180         var rangeEndRange = range.cloneRange();
41181         rangeEndRange.collapse(false);
41182     
41183         var nodeStartRange = nodeRange.cloneRange();
41184         nodeStartRange.collapse(true);
41185     
41186         var nodeEndRange = nodeRange.cloneRange();
41187         nodeEndRange.collapse(false);
41188     
41189         return rangeStartRange.compareBoundaryPoints(
41190                  Range.START_TO_START, nodeEndRange) == -1 &&
41191                rangeEndRange.compareBoundaryPoints(
41192                  Range.START_TO_START, nodeStartRange) == 1;
41193         
41194          
41195     },
41196     rangeCompareNode : function(range, node)
41197     {
41198         var nodeRange = node.ownerDocument.createRange();
41199         try {
41200             nodeRange.selectNode(node);
41201         } catch (e) {
41202             nodeRange.selectNodeContents(node);
41203         }
41204         
41205         
41206         range.collapse(true);
41207     
41208         nodeRange.collapse(true);
41209      
41210         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41211         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41212          
41213         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41214         
41215         var nodeIsBefore   =  ss == 1;
41216         var nodeIsAfter    = ee == -1;
41217         
41218         if (nodeIsBefore && nodeIsAfter)
41219             return 0; // outer
41220         if (!nodeIsBefore && nodeIsAfter)
41221             return 1; //right trailed.
41222         
41223         if (nodeIsBefore && !nodeIsAfter)
41224             return 2;  // left trailed.
41225         // fully contined.
41226         return 3;
41227     },
41228
41229     // private? - in a new class?
41230     cleanUpPaste :  function()
41231     {
41232         // cleans up the whole document..
41233          Roo.log('cleanuppaste');
41234         this.cleanUpChildren(this.doc.body);
41235         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41236         if (clean != this.doc.body.innerHTML) {
41237             this.doc.body.innerHTML = clean;
41238         }
41239         
41240     },
41241     
41242     cleanWordChars : function(input) {// change the chars to hex code
41243         var he = Roo.form.HtmlEditor;
41244         
41245         var output = input;
41246         Roo.each(he.swapCodes, function(sw) { 
41247             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41248             
41249             output = output.replace(swapper, sw[1]);
41250         });
41251         
41252         return output;
41253     },
41254     
41255     
41256     cleanUpChildren : function (n)
41257     {
41258         if (!n.childNodes.length) {
41259             return;
41260         }
41261         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41262            this.cleanUpChild(n.childNodes[i]);
41263         }
41264     },
41265     
41266     
41267         
41268     
41269     cleanUpChild : function (node)
41270     {
41271         var ed = this;
41272         //console.log(node);
41273         if (node.nodeName == "#text") {
41274             // clean up silly Windows -- stuff?
41275             return; 
41276         }
41277         if (node.nodeName == "#comment") {
41278             node.parentNode.removeChild(node);
41279             // clean up silly Windows -- stuff?
41280             return; 
41281         }
41282         
41283         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
41284             // remove node.
41285             node.parentNode.removeChild(node);
41286             return;
41287             
41288         }
41289         
41290         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
41291         
41292         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41293         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41294         
41295         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41296         //    remove_keep_children = true;
41297         //}
41298         
41299         if (remove_keep_children) {
41300             this.cleanUpChildren(node);
41301             // inserts everything just before this node...
41302             while (node.childNodes.length) {
41303                 var cn = node.childNodes[0];
41304                 node.removeChild(cn);
41305                 node.parentNode.insertBefore(cn, node);
41306             }
41307             node.parentNode.removeChild(node);
41308             return;
41309         }
41310         
41311         if (!node.attributes || !node.attributes.length) {
41312             this.cleanUpChildren(node);
41313             return;
41314         }
41315         
41316         function cleanAttr(n,v)
41317         {
41318             
41319             if (v.match(/^\./) || v.match(/^\//)) {
41320                 return;
41321             }
41322             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41323                 return;
41324             }
41325             if (v.match(/^#/)) {
41326                 return;
41327             }
41328 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41329             node.removeAttribute(n);
41330             
41331         }
41332         
41333         function cleanStyle(n,v)
41334         {
41335             if (v.match(/expression/)) { //XSS?? should we even bother..
41336                 node.removeAttribute(n);
41337                 return;
41338             }
41339             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
41340             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
41341             
41342             
41343             var parts = v.split(/;/);
41344             var clean = [];
41345             
41346             Roo.each(parts, function(p) {
41347                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
41348                 if (!p.length) {
41349                     return true;
41350                 }
41351                 var l = p.split(':').shift().replace(/\s+/g,'');
41352                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
41353                 
41354                 
41355                 if ( cblack.indexOf(l) > -1) {
41356 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41357                     //node.removeAttribute(n);
41358                     return true;
41359                 }
41360                 //Roo.log()
41361                 // only allow 'c whitelisted system attributes'
41362                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
41363 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41364                     //node.removeAttribute(n);
41365                     return true;
41366                 }
41367                 
41368                 
41369                  
41370                 
41371                 clean.push(p);
41372                 return true;
41373             });
41374             if (clean.length) { 
41375                 node.setAttribute(n, clean.join(';'));
41376             } else {
41377                 node.removeAttribute(n);
41378             }
41379             
41380         }
41381         
41382         
41383         for (var i = node.attributes.length-1; i > -1 ; i--) {
41384             var a = node.attributes[i];
41385             //console.log(a);
41386             
41387             if (a.name.toLowerCase().substr(0,2)=='on')  {
41388                 node.removeAttribute(a.name);
41389                 continue;
41390             }
41391             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
41392                 node.removeAttribute(a.name);
41393                 continue;
41394             }
41395             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
41396                 cleanAttr(a.name,a.value); // fixme..
41397                 continue;
41398             }
41399             if (a.name == 'style') {
41400                 cleanStyle(a.name,a.value);
41401                 continue;
41402             }
41403             /// clean up MS crap..
41404             // tecnically this should be a list of valid class'es..
41405             
41406             
41407             if (a.name == 'class') {
41408                 if (a.value.match(/^Mso/)) {
41409                     node.className = '';
41410                 }
41411                 
41412                 if (a.value.match(/body/)) {
41413                     node.className = '';
41414                 }
41415                 continue;
41416             }
41417             
41418             // style cleanup!?
41419             // class cleanup?
41420             
41421         }
41422         
41423         
41424         this.cleanUpChildren(node);
41425         
41426         
41427     }
41428     
41429     
41430     // hide stuff that is not compatible
41431     /**
41432      * @event blur
41433      * @hide
41434      */
41435     /**
41436      * @event change
41437      * @hide
41438      */
41439     /**
41440      * @event focus
41441      * @hide
41442      */
41443     /**
41444      * @event specialkey
41445      * @hide
41446      */
41447     /**
41448      * @cfg {String} fieldClass @hide
41449      */
41450     /**
41451      * @cfg {String} focusClass @hide
41452      */
41453     /**
41454      * @cfg {String} autoCreate @hide
41455      */
41456     /**
41457      * @cfg {String} inputType @hide
41458      */
41459     /**
41460      * @cfg {String} invalidClass @hide
41461      */
41462     /**
41463      * @cfg {String} invalidText @hide
41464      */
41465     /**
41466      * @cfg {String} msgFx @hide
41467      */
41468     /**
41469      * @cfg {String} validateOnBlur @hide
41470      */
41471 });
41472
41473 Roo.form.HtmlEditor.white = [
41474         'area', 'br', 'img', 'input', 'hr', 'wbr',
41475         
41476        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
41477        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
41478        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
41479        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
41480        'table',   'ul',         'xmp', 
41481        
41482        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
41483       'thead',   'tr', 
41484      
41485       'dir', 'menu', 'ol', 'ul', 'dl',
41486        
41487       'embed',  'object'
41488 ];
41489
41490
41491 Roo.form.HtmlEditor.black = [
41492     //    'embed',  'object', // enable - backend responsiblity to clean thiese
41493         'applet', // 
41494         'base',   'basefont', 'bgsound', 'blink',  'body', 
41495         'frame',  'frameset', 'head',    'html',   'ilayer', 
41496         'iframe', 'layer',  'link',     'meta',    'object',   
41497         'script', 'style' ,'title',  'xml' // clean later..
41498 ];
41499 Roo.form.HtmlEditor.clean = [
41500     'script', 'style', 'title', 'xml'
41501 ];
41502 Roo.form.HtmlEditor.remove = [
41503     'font'
41504 ];
41505 // attributes..
41506
41507 Roo.form.HtmlEditor.ablack = [
41508     'on'
41509 ];
41510     
41511 Roo.form.HtmlEditor.aclean = [ 
41512     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
41513 ];
41514
41515 // protocols..
41516 Roo.form.HtmlEditor.pwhite= [
41517         'http',  'https',  'mailto'
41518 ];
41519
41520 // white listed style attributes.
41521 Roo.form.HtmlEditor.cwhite= [
41522       //  'text-align', /// default is to allow most things..
41523       
41524          
41525 //        'font-size'//??
41526 ];
41527
41528 // black listed style attributes.
41529 Roo.form.HtmlEditor.cblack= [
41530       //  'font-size' -- this can be set by the project 
41531 ];
41532
41533
41534 Roo.form.HtmlEditor.swapCodes   =[ 
41535     [    8211, "--" ], 
41536     [    8212, "--" ], 
41537     [    8216,  "'" ],  
41538     [    8217, "'" ],  
41539     [    8220, '"' ],  
41540     [    8221, '"' ],  
41541     [    8226, "*" ],  
41542     [    8230, "..." ]
41543 ]; 
41544
41545     // <script type="text/javascript">
41546 /*
41547  * Based on
41548  * Ext JS Library 1.1.1
41549  * Copyright(c) 2006-2007, Ext JS, LLC.
41550  *  
41551  
41552  */
41553
41554 /**
41555  * @class Roo.form.HtmlEditorToolbar1
41556  * Basic Toolbar
41557  * 
41558  * Usage:
41559  *
41560  new Roo.form.HtmlEditor({
41561     ....
41562     toolbars : [
41563         new Roo.form.HtmlEditorToolbar1({
41564             disable : { fonts: 1 , format: 1, ..., ... , ...],
41565             btns : [ .... ]
41566         })
41567     }
41568      
41569  * 
41570  * @cfg {Object} disable List of elements to disable..
41571  * @cfg {Array} btns List of additional buttons.
41572  * 
41573  * 
41574  * NEEDS Extra CSS? 
41575  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
41576  */
41577  
41578 Roo.form.HtmlEditor.ToolbarStandard = function(config)
41579 {
41580     
41581     Roo.apply(this, config);
41582     
41583     // default disabled, based on 'good practice'..
41584     this.disable = this.disable || {};
41585     Roo.applyIf(this.disable, {
41586         fontSize : true,
41587         colors : true,
41588         specialElements : true
41589     });
41590     
41591     
41592     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
41593     // dont call parent... till later.
41594 }
41595
41596 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
41597     
41598     tb: false,
41599     
41600     rendered: false,
41601     
41602     editor : false,
41603     /**
41604      * @cfg {Object} disable  List of toolbar elements to disable
41605          
41606      */
41607     disable : false,
41608       /**
41609      * @cfg {Array} fontFamilies An array of available font families
41610      */
41611     fontFamilies : [
41612         'Arial',
41613         'Courier New',
41614         'Tahoma',
41615         'Times New Roman',
41616         'Verdana'
41617     ],
41618     
41619     specialChars : [
41620            "&#169;",
41621           "&#174;",     
41622           "&#8482;",    
41623           "&#163;" ,    
41624          // "&#8212;",    
41625           "&#8230;",    
41626           "&#247;" ,    
41627         //  "&#225;" ,     ?? a acute?
41628            "&#8364;"    , //Euro
41629        //   "&#8220;"    ,
41630         //  "&#8221;"    ,
41631         //  "&#8226;"    ,
41632           "&#176;"  //   , // degrees
41633
41634          // "&#233;"     , // e ecute
41635          // "&#250;"     , // u ecute?
41636     ],
41637     
41638     specialElements : [
41639         {
41640             text: "Insert Table",
41641             xtype: 'MenuItem',
41642             xns : Roo.Menu,
41643             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
41644                 
41645         },
41646         {    
41647             text: "Insert Image",
41648             xtype: 'MenuItem',
41649             xns : Roo.Menu,
41650             ihtml : '<img src="about:blank"/>'
41651             
41652         }
41653         
41654          
41655     ],
41656     
41657     
41658     inputElements : [ 
41659             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
41660             "input:submit", "input:button", "select", "textarea", "label" ],
41661     formats : [
41662         ["p"] ,  
41663         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
41664         ["pre"],[ "code"], 
41665         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
41666         ['div'],['span']
41667     ],
41668      /**
41669      * @cfg {String} defaultFont default font to use.
41670      */
41671     defaultFont: 'tahoma',
41672    
41673     fontSelect : false,
41674     
41675     
41676     formatCombo : false,
41677     
41678     init : function(editor)
41679     {
41680         this.editor = editor;
41681         
41682         
41683         var fid = editor.frameId;
41684         var etb = this;
41685         function btn(id, toggle, handler){
41686             var xid = fid + '-'+ id ;
41687             return {
41688                 id : xid,
41689                 cmd : id,
41690                 cls : 'x-btn-icon x-edit-'+id,
41691                 enableToggle:toggle !== false,
41692                 scope: editor, // was editor...
41693                 handler:handler||editor.relayBtnCmd,
41694                 clickEvent:'mousedown',
41695                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
41696                 tabIndex:-1
41697             };
41698         }
41699         
41700         
41701         
41702         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
41703         this.tb = tb;
41704          // stop form submits
41705         tb.el.on('click', function(e){
41706             e.preventDefault(); // what does this do?
41707         });
41708
41709         if(!this.disable.font) { // && !Roo.isSafari){
41710             /* why no safari for fonts 
41711             editor.fontSelect = tb.el.createChild({
41712                 tag:'select',
41713                 tabIndex: -1,
41714                 cls:'x-font-select',
41715                 html: this.createFontOptions()
41716             });
41717             
41718             editor.fontSelect.on('change', function(){
41719                 var font = editor.fontSelect.dom.value;
41720                 editor.relayCmd('fontname', font);
41721                 editor.deferFocus();
41722             }, editor);
41723             
41724             tb.add(
41725                 editor.fontSelect.dom,
41726                 '-'
41727             );
41728             */
41729             
41730         };
41731         if(!this.disable.formats){
41732             this.formatCombo = new Roo.form.ComboBox({
41733                 store: new Roo.data.SimpleStore({
41734                     id : 'tag',
41735                     fields: ['tag'],
41736                     data : this.formats // from states.js
41737                 }),
41738                 blockFocus : true,
41739                 name : '',
41740                 //autoCreate : {tag: "div",  size: "20"},
41741                 displayField:'tag',
41742                 typeAhead: false,
41743                 mode: 'local',
41744                 editable : false,
41745                 triggerAction: 'all',
41746                 emptyText:'Add tag',
41747                 selectOnFocus:true,
41748                 width:135,
41749                 listeners : {
41750                     'select': function(c, r, i) {
41751                         editor.insertTag(r.get('tag'));
41752                         editor.focus();
41753                     }
41754                 }
41755
41756             });
41757             tb.addField(this.formatCombo);
41758             
41759         }
41760         
41761         if(!this.disable.format){
41762             tb.add(
41763                 btn('bold'),
41764                 btn('italic'),
41765                 btn('underline')
41766             );
41767         };
41768         if(!this.disable.fontSize){
41769             tb.add(
41770                 '-',
41771                 
41772                 
41773                 btn('increasefontsize', false, editor.adjustFont),
41774                 btn('decreasefontsize', false, editor.adjustFont)
41775             );
41776         };
41777         
41778         
41779         if(!this.disable.colors){
41780             tb.add(
41781                 '-', {
41782                     id:editor.frameId +'-forecolor',
41783                     cls:'x-btn-icon x-edit-forecolor',
41784                     clickEvent:'mousedown',
41785                     tooltip: this.buttonTips['forecolor'] || undefined,
41786                     tabIndex:-1,
41787                     menu : new Roo.menu.ColorMenu({
41788                         allowReselect: true,
41789                         focus: Roo.emptyFn,
41790                         value:'000000',
41791                         plain:true,
41792                         selectHandler: function(cp, color){
41793                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
41794                             editor.deferFocus();
41795                         },
41796                         scope: editor,
41797                         clickEvent:'mousedown'
41798                     })
41799                 }, {
41800                     id:editor.frameId +'backcolor',
41801                     cls:'x-btn-icon x-edit-backcolor',
41802                     clickEvent:'mousedown',
41803                     tooltip: this.buttonTips['backcolor'] || undefined,
41804                     tabIndex:-1,
41805                     menu : new Roo.menu.ColorMenu({
41806                         focus: Roo.emptyFn,
41807                         value:'FFFFFF',
41808                         plain:true,
41809                         allowReselect: true,
41810                         selectHandler: function(cp, color){
41811                             if(Roo.isGecko){
41812                                 editor.execCmd('useCSS', false);
41813                                 editor.execCmd('hilitecolor', color);
41814                                 editor.execCmd('useCSS', true);
41815                                 editor.deferFocus();
41816                             }else{
41817                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
41818                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
41819                                 editor.deferFocus();
41820                             }
41821                         },
41822                         scope:editor,
41823                         clickEvent:'mousedown'
41824                     })
41825                 }
41826             );
41827         };
41828         // now add all the items...
41829         
41830
41831         if(!this.disable.alignments){
41832             tb.add(
41833                 '-',
41834                 btn('justifyleft'),
41835                 btn('justifycenter'),
41836                 btn('justifyright')
41837             );
41838         };
41839
41840         //if(!Roo.isSafari){
41841             if(!this.disable.links){
41842                 tb.add(
41843                     '-',
41844                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
41845                 );
41846             };
41847
41848             if(!this.disable.lists){
41849                 tb.add(
41850                     '-',
41851                     btn('insertorderedlist'),
41852                     btn('insertunorderedlist')
41853                 );
41854             }
41855             if(!this.disable.sourceEdit){
41856                 tb.add(
41857                     '-',
41858                     btn('sourceedit', true, function(btn){
41859                         this.toggleSourceEdit(btn.pressed);
41860                     })
41861                 );
41862             }
41863         //}
41864         
41865         var smenu = { };
41866         // special menu.. - needs to be tidied up..
41867         if (!this.disable.special) {
41868             smenu = {
41869                 text: "&#169;",
41870                 cls: 'x-edit-none',
41871                 
41872                 menu : {
41873                     items : []
41874                 }
41875             };
41876             for (var i =0; i < this.specialChars.length; i++) {
41877                 smenu.menu.items.push({
41878                     
41879                     html: this.specialChars[i],
41880                     handler: function(a,b) {
41881                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
41882                         //editor.insertAtCursor(a.html);
41883                         
41884                     },
41885                     tabIndex:-1
41886                 });
41887             }
41888             
41889             
41890             tb.add(smenu);
41891             
41892             
41893         }
41894          
41895         if (!this.disable.specialElements) {
41896             var semenu = {
41897                 text: "Other;",
41898                 cls: 'x-edit-none',
41899                 menu : {
41900                     items : []
41901                 }
41902             };
41903             for (var i =0; i < this.specialElements.length; i++) {
41904                 semenu.menu.items.push(
41905                     Roo.apply({ 
41906                         handler: function(a,b) {
41907                             editor.insertAtCursor(this.ihtml);
41908                         }
41909                     }, this.specialElements[i])
41910                 );
41911                     
41912             }
41913             
41914             tb.add(semenu);
41915             
41916             
41917         }
41918          
41919         
41920         if (this.btns) {
41921             for(var i =0; i< this.btns.length;i++) {
41922                 var b = Roo.factory(this.btns[i],Roo.form);
41923                 b.cls =  'x-edit-none';
41924                 b.scope = editor;
41925                 tb.add(b);
41926             }
41927         
41928         }
41929         
41930         
41931         
41932         // disable everything...
41933         
41934         this.tb.items.each(function(item){
41935            if(item.id != editor.frameId+ '-sourceedit'){
41936                 item.disable();
41937             }
41938         });
41939         this.rendered = true;
41940         
41941         // the all the btns;
41942         editor.on('editorevent', this.updateToolbar, this);
41943         // other toolbars need to implement this..
41944         //editor.on('editmodechange', this.updateToolbar, this);
41945     },
41946     
41947     
41948     
41949     /**
41950      * Protected method that will not generally be called directly. It triggers
41951      * a toolbar update by reading the markup state of the current selection in the editor.
41952      */
41953     updateToolbar: function(){
41954
41955         if(!this.editor.activated){
41956             this.editor.onFirstFocus();
41957             return;
41958         }
41959
41960         var btns = this.tb.items.map, 
41961             doc = this.editor.doc,
41962             frameId = this.editor.frameId;
41963
41964         if(!this.disable.font && !Roo.isSafari){
41965             /*
41966             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
41967             if(name != this.fontSelect.dom.value){
41968                 this.fontSelect.dom.value = name;
41969             }
41970             */
41971         }
41972         if(!this.disable.format){
41973             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
41974             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
41975             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
41976         }
41977         if(!this.disable.alignments){
41978             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
41979             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
41980             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
41981         }
41982         if(!Roo.isSafari && !this.disable.lists){
41983             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
41984             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
41985         }
41986         
41987         var ans = this.editor.getAllAncestors();
41988         if (this.formatCombo) {
41989             
41990             
41991             var store = this.formatCombo.store;
41992             this.formatCombo.setValue("");
41993             for (var i =0; i < ans.length;i++) {
41994                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
41995                     // select it..
41996                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
41997                     break;
41998                 }
41999             }
42000         }
42001         
42002         
42003         
42004         // hides menus... - so this cant be on a menu...
42005         Roo.menu.MenuMgr.hideAll();
42006
42007         //this.editorsyncValue();
42008     },
42009    
42010     
42011     createFontOptions : function(){
42012         var buf = [], fs = this.fontFamilies, ff, lc;
42013         
42014         
42015         
42016         for(var i = 0, len = fs.length; i< len; i++){
42017             ff = fs[i];
42018             lc = ff.toLowerCase();
42019             buf.push(
42020                 '<option value="',lc,'" style="font-family:',ff,';"',
42021                     (this.defaultFont == lc ? ' selected="true">' : '>'),
42022                     ff,
42023                 '</option>'
42024             );
42025         }
42026         return buf.join('');
42027     },
42028     
42029     toggleSourceEdit : function(sourceEditMode){
42030         if(sourceEditMode === undefined){
42031             sourceEditMode = !this.sourceEditMode;
42032         }
42033         this.sourceEditMode = sourceEditMode === true;
42034         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
42035         // just toggle the button?
42036         if(btn.pressed !== this.editor.sourceEditMode){
42037             btn.toggle(this.editor.sourceEditMode);
42038             return;
42039         }
42040         
42041         if(this.sourceEditMode){
42042             this.tb.items.each(function(item){
42043                 if(item.cmd != 'sourceedit'){
42044                     item.disable();
42045                 }
42046             });
42047           
42048         }else{
42049             if(this.initialized){
42050                 this.tb.items.each(function(item){
42051                     item.enable();
42052                 });
42053             }
42054             
42055         }
42056         // tell the editor that it's been pressed..
42057         this.editor.toggleSourceEdit(sourceEditMode);
42058        
42059     },
42060      /**
42061      * Object collection of toolbar tooltips for the buttons in the editor. The key
42062      * is the command id associated with that button and the value is a valid QuickTips object.
42063      * For example:
42064 <pre><code>
42065 {
42066     bold : {
42067         title: 'Bold (Ctrl+B)',
42068         text: 'Make the selected text bold.',
42069         cls: 'x-html-editor-tip'
42070     },
42071     italic : {
42072         title: 'Italic (Ctrl+I)',
42073         text: 'Make the selected text italic.',
42074         cls: 'x-html-editor-tip'
42075     },
42076     ...
42077 </code></pre>
42078     * @type Object
42079      */
42080     buttonTips : {
42081         bold : {
42082             title: 'Bold (Ctrl+B)',
42083             text: 'Make the selected text bold.',
42084             cls: 'x-html-editor-tip'
42085         },
42086         italic : {
42087             title: 'Italic (Ctrl+I)',
42088             text: 'Make the selected text italic.',
42089             cls: 'x-html-editor-tip'
42090         },
42091         underline : {
42092             title: 'Underline (Ctrl+U)',
42093             text: 'Underline the selected text.',
42094             cls: 'x-html-editor-tip'
42095         },
42096         increasefontsize : {
42097             title: 'Grow Text',
42098             text: 'Increase the font size.',
42099             cls: 'x-html-editor-tip'
42100         },
42101         decreasefontsize : {
42102             title: 'Shrink Text',
42103             text: 'Decrease the font size.',
42104             cls: 'x-html-editor-tip'
42105         },
42106         backcolor : {
42107             title: 'Text Highlight Color',
42108             text: 'Change the background color of the selected text.',
42109             cls: 'x-html-editor-tip'
42110         },
42111         forecolor : {
42112             title: 'Font Color',
42113             text: 'Change the color of the selected text.',
42114             cls: 'x-html-editor-tip'
42115         },
42116         justifyleft : {
42117             title: 'Align Text Left',
42118             text: 'Align text to the left.',
42119             cls: 'x-html-editor-tip'
42120         },
42121         justifycenter : {
42122             title: 'Center Text',
42123             text: 'Center text in the editor.',
42124             cls: 'x-html-editor-tip'
42125         },
42126         justifyright : {
42127             title: 'Align Text Right',
42128             text: 'Align text to the right.',
42129             cls: 'x-html-editor-tip'
42130         },
42131         insertunorderedlist : {
42132             title: 'Bullet List',
42133             text: 'Start a bulleted list.',
42134             cls: 'x-html-editor-tip'
42135         },
42136         insertorderedlist : {
42137             title: 'Numbered List',
42138             text: 'Start a numbered list.',
42139             cls: 'x-html-editor-tip'
42140         },
42141         createlink : {
42142             title: 'Hyperlink',
42143             text: 'Make the selected text a hyperlink.',
42144             cls: 'x-html-editor-tip'
42145         },
42146         sourceedit : {
42147             title: 'Source Edit',
42148             text: 'Switch to source editing mode.',
42149             cls: 'x-html-editor-tip'
42150         }
42151     },
42152     // private
42153     onDestroy : function(){
42154         if(this.rendered){
42155             
42156             this.tb.items.each(function(item){
42157                 if(item.menu){
42158                     item.menu.removeAll();
42159                     if(item.menu.el){
42160                         item.menu.el.destroy();
42161                     }
42162                 }
42163                 item.destroy();
42164             });
42165              
42166         }
42167     },
42168     onFirstFocus: function() {
42169         this.tb.items.each(function(item){
42170            item.enable();
42171         });
42172     }
42173 });
42174
42175
42176
42177
42178 // <script type="text/javascript">
42179 /*
42180  * Based on
42181  * Ext JS Library 1.1.1
42182  * Copyright(c) 2006-2007, Ext JS, LLC.
42183  *  
42184  
42185  */
42186
42187  
42188 /**
42189  * @class Roo.form.HtmlEditor.ToolbarContext
42190  * Context Toolbar
42191  * 
42192  * Usage:
42193  *
42194  new Roo.form.HtmlEditor({
42195     ....
42196     toolbars : [
42197         { xtype: 'ToolbarStandard', styles : {} }
42198         { xtype: 'ToolbarContext', disable : {} }
42199     ]
42200 })
42201
42202      
42203  * 
42204  * @config : {Object} disable List of elements to disable.. (not done yet.)
42205  * @config : {Object} styles  Map of styles available.
42206  * 
42207  */
42208
42209 Roo.form.HtmlEditor.ToolbarContext = function(config)
42210 {
42211     
42212     Roo.apply(this, config);
42213     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42214     // dont call parent... till later.
42215     this.styles = this.styles || {};
42216 }
42217
42218  
42219
42220 Roo.form.HtmlEditor.ToolbarContext.types = {
42221     'IMG' : {
42222         width : {
42223             title: "Width",
42224             width: 40
42225         },
42226         height:  {
42227             title: "Height",
42228             width: 40
42229         },
42230         align: {
42231             title: "Align",
42232             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
42233             width : 80
42234             
42235         },
42236         border: {
42237             title: "Border",
42238             width: 40
42239         },
42240         alt: {
42241             title: "Alt",
42242             width: 120
42243         },
42244         src : {
42245             title: "Src",
42246             width: 220
42247         }
42248         
42249     },
42250     'A' : {
42251         name : {
42252             title: "Name",
42253             width: 50
42254         },
42255         href:  {
42256             title: "Href",
42257             width: 220
42258         } // border?
42259         
42260     },
42261     'TABLE' : {
42262         rows : {
42263             title: "Rows",
42264             width: 20
42265         },
42266         cols : {
42267             title: "Cols",
42268             width: 20
42269         },
42270         width : {
42271             title: "Width",
42272             width: 40
42273         },
42274         height : {
42275             title: "Height",
42276             width: 40
42277         },
42278         border : {
42279             title: "Border",
42280             width: 20
42281         }
42282     },
42283     'TD' : {
42284         width : {
42285             title: "Width",
42286             width: 40
42287         },
42288         height : {
42289             title: "Height",
42290             width: 40
42291         },   
42292         align: {
42293             title: "Align",
42294             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
42295             width: 80
42296         },
42297         valign: {
42298             title: "Valign",
42299             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
42300             width: 80
42301         },
42302         colspan: {
42303             title: "Colspan",
42304             width: 20
42305             
42306         },
42307          'font-family'  : {
42308             title : "Font",
42309             style : 'fontFamily',
42310             displayField: 'display',
42311             optname : 'font-family',
42312             width: 140
42313         }
42314     },
42315     'INPUT' : {
42316         name : {
42317             title: "name",
42318             width: 120
42319         },
42320         value : {
42321             title: "Value",
42322             width: 120
42323         },
42324         width : {
42325             title: "Width",
42326             width: 40
42327         }
42328     },
42329     'LABEL' : {
42330         'for' : {
42331             title: "For",
42332             width: 120
42333         }
42334     },
42335     'TEXTAREA' : {
42336           name : {
42337             title: "name",
42338             width: 120
42339         },
42340         rows : {
42341             title: "Rows",
42342             width: 20
42343         },
42344         cols : {
42345             title: "Cols",
42346             width: 20
42347         }
42348     },
42349     'SELECT' : {
42350         name : {
42351             title: "name",
42352             width: 120
42353         },
42354         selectoptions : {
42355             title: "Options",
42356             width: 200
42357         }
42358     },
42359     
42360     // should we really allow this??
42361     // should this just be 
42362     'BODY' : {
42363         title : {
42364             title: "Title",
42365             width: 200,
42366             disabled : true
42367         }
42368     },
42369     'SPAN' : {
42370         'font-family'  : {
42371             title : "Font",
42372             style : 'fontFamily',
42373             displayField: 'display',
42374             optname : 'font-family',
42375             width: 140
42376         }
42377     },
42378     'DIV' : {
42379         'font-family'  : {
42380             title : "Font",
42381             style : 'fontFamily',
42382             displayField: 'display',
42383             optname : 'font-family',
42384             width: 140
42385         }
42386     },
42387      'P' : {
42388         'font-family'  : {
42389             title : "Font",
42390             style : 'fontFamily',
42391             displayField: 'display',
42392             optname : 'font-family',
42393             width: 140
42394         }
42395     },
42396     
42397     '*' : {
42398         // empty..
42399     }
42400
42401 };
42402
42403 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
42404 Roo.form.HtmlEditor.ToolbarContext.stores = false;
42405
42406 Roo.form.HtmlEditor.ToolbarContext.options = {
42407         'font-family'  : [ 
42408                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
42409                 [ 'Courier New', 'Courier New'],
42410                 [ 'Tahoma', 'Tahoma'],
42411                 [ 'Times New Roman,serif', 'Times'],
42412                 [ 'Verdana','Verdana' ]
42413         ]
42414 };
42415
42416 // fixme - these need to be configurable..
42417  
42418
42419 Roo.form.HtmlEditor.ToolbarContext.types
42420
42421
42422 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
42423     
42424     tb: false,
42425     
42426     rendered: false,
42427     
42428     editor : false,
42429     /**
42430      * @cfg {Object} disable  List of toolbar elements to disable
42431          
42432      */
42433     disable : false,
42434     /**
42435      * @cfg {Object} styles List of styles 
42436      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
42437      *
42438      * These must be defined in the page, so they get rendered correctly..
42439      * .headline { }
42440      * TD.underline { }
42441      * 
42442      */
42443     styles : false,
42444     
42445     options: false,
42446     
42447     toolbars : false,
42448     
42449     init : function(editor)
42450     {
42451         this.editor = editor;
42452         
42453         
42454         var fid = editor.frameId;
42455         var etb = this;
42456         function btn(id, toggle, handler){
42457             var xid = fid + '-'+ id ;
42458             return {
42459                 id : xid,
42460                 cmd : id,
42461                 cls : 'x-btn-icon x-edit-'+id,
42462                 enableToggle:toggle !== false,
42463                 scope: editor, // was editor...
42464                 handler:handler||editor.relayBtnCmd,
42465                 clickEvent:'mousedown',
42466                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42467                 tabIndex:-1
42468             };
42469         }
42470         // create a new element.
42471         var wdiv = editor.wrap.createChild({
42472                 tag: 'div'
42473             }, editor.wrap.dom.firstChild.nextSibling, true);
42474         
42475         // can we do this more than once??
42476         
42477          // stop form submits
42478       
42479  
42480         // disable everything...
42481         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
42482         this.toolbars = {};
42483            
42484         for (var i in  ty) {
42485           
42486             this.toolbars[i] = this.buildToolbar(ty[i],i);
42487         }
42488         this.tb = this.toolbars.BODY;
42489         this.tb.el.show();
42490         this.buildFooter();
42491         this.footer.show();
42492         editor.on('hide', function( ) { this.footer.hide() }, this);
42493         editor.on('show', function( ) { this.footer.show() }, this);
42494         
42495          
42496         this.rendered = true;
42497         
42498         // the all the btns;
42499         editor.on('editorevent', this.updateToolbar, this);
42500         // other toolbars need to implement this..
42501         //editor.on('editmodechange', this.updateToolbar, this);
42502     },
42503     
42504     
42505     
42506     /**
42507      * Protected method that will not generally be called directly. It triggers
42508      * a toolbar update by reading the markup state of the current selection in the editor.
42509      */
42510     updateToolbar: function(editor,ev,sel){
42511
42512         //Roo.log(ev);
42513         // capture mouse up - this is handy for selecting images..
42514         // perhaps should go somewhere else...
42515         if(!this.editor.activated){
42516              this.editor.onFirstFocus();
42517             return;
42518         }
42519         
42520         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
42521         // selectNode - might want to handle IE?
42522         if (ev &&
42523             (ev.type == 'mouseup' || ev.type == 'click' ) &&
42524             ev.target && ev.target.tagName == 'IMG') {
42525             // they have click on an image...
42526             // let's see if we can change the selection...
42527             sel = ev.target;
42528          
42529               var nodeRange = sel.ownerDocument.createRange();
42530             try {
42531                 nodeRange.selectNode(sel);
42532             } catch (e) {
42533                 nodeRange.selectNodeContents(sel);
42534             }
42535             //nodeRange.collapse(true);
42536             var s = editor.win.getSelection();
42537             s.removeAllRanges();
42538             s.addRange(nodeRange);
42539         }  
42540         
42541       
42542         var updateFooter = sel ? false : true;
42543         
42544         
42545         var ans = this.editor.getAllAncestors();
42546         
42547         // pick
42548         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
42549         
42550         if (!sel) { 
42551             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
42552             sel = sel ? sel : this.editor.doc.body;
42553             sel = sel.tagName.length ? sel : this.editor.doc.body;
42554             
42555         }
42556         // pick a menu that exists..
42557         var tn = sel.tagName.toUpperCase();
42558         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
42559         
42560         tn = sel.tagName.toUpperCase();
42561         
42562         var lastSel = this.tb.selectedNode
42563         
42564         this.tb.selectedNode = sel;
42565         
42566         // if current menu does not match..
42567         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
42568                 
42569             this.tb.el.hide();
42570             ///console.log("show: " + tn);
42571             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
42572             this.tb.el.show();
42573             // update name
42574             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
42575             
42576             
42577             // update attributes
42578             if (this.tb.fields) {
42579                 this.tb.fields.each(function(e) {
42580                     if (e.stylename) {
42581                         e.setValue(sel.style[e.stylename]);
42582                         return;
42583                     } 
42584                    e.setValue(sel.getAttribute(e.attrname));
42585                 });
42586             }
42587             
42588             var hasStyles = false;
42589             for(var i in this.styles) {
42590                 hasStyles = true;
42591                 break;
42592             }
42593             
42594             // update styles
42595             if (hasStyles) { 
42596                 var st = this.tb.fields.item(0);
42597                 
42598                 st.store.removeAll();
42599                
42600                 
42601                 var cn = sel.className.split(/\s+/);
42602                 
42603                 var avs = [];
42604                 if (this.styles['*']) {
42605                     
42606                     Roo.each(this.styles['*'], function(v) {
42607                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
42608                     });
42609                 }
42610                 if (this.styles[tn]) { 
42611                     Roo.each(this.styles[tn], function(v) {
42612                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
42613                     });
42614                 }
42615                 
42616                 st.store.loadData(avs);
42617                 st.collapse();
42618                 st.setValue(cn);
42619             }
42620             // flag our selected Node.
42621             this.tb.selectedNode = sel;
42622            
42623            
42624             Roo.menu.MenuMgr.hideAll();
42625
42626         }
42627         
42628         if (!updateFooter) {
42629             //this.footDisp.dom.innerHTML = ''; 
42630             return;
42631         }
42632         // update the footer
42633         //
42634         var html = '';
42635         
42636         this.footerEls = ans.reverse();
42637         Roo.each(this.footerEls, function(a,i) {
42638             if (!a) { return; }
42639             html += html.length ? ' &gt; '  :  '';
42640             
42641             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
42642             
42643         });
42644        
42645         // 
42646         var sz = this.footDisp.up('td').getSize();
42647         this.footDisp.dom.style.width = (sz.width -10) + 'px';
42648         this.footDisp.dom.style.marginLeft = '5px';
42649         
42650         this.footDisp.dom.style.overflow = 'hidden';
42651         
42652         this.footDisp.dom.innerHTML = html;
42653             
42654         //this.editorsyncValue();
42655     },
42656      
42657     
42658    
42659        
42660     // private
42661     onDestroy : function(){
42662         if(this.rendered){
42663             
42664             this.tb.items.each(function(item){
42665                 if(item.menu){
42666                     item.menu.removeAll();
42667                     if(item.menu.el){
42668                         item.menu.el.destroy();
42669                     }
42670                 }
42671                 item.destroy();
42672             });
42673              
42674         }
42675     },
42676     onFirstFocus: function() {
42677         // need to do this for all the toolbars..
42678         this.tb.items.each(function(item){
42679            item.enable();
42680         });
42681     },
42682     buildToolbar: function(tlist, nm)
42683     {
42684         var editor = this.editor;
42685          // create a new element.
42686         var wdiv = editor.wrap.createChild({
42687                 tag: 'div'
42688             }, editor.wrap.dom.firstChild.nextSibling, true);
42689         
42690        
42691         var tb = new Roo.Toolbar(wdiv);
42692         // add the name..
42693         
42694         tb.add(nm+ ":&nbsp;");
42695         
42696         var styles = [];
42697         for(var i in this.styles) {
42698             styles.push(i);
42699         }
42700         
42701         // styles...
42702         if (styles && styles.length) {
42703             
42704             // this needs a multi-select checkbox...
42705             tb.addField( new Roo.form.ComboBox({
42706                 store: new Roo.data.SimpleStore({
42707                     id : 'val',
42708                     fields: ['val', 'selected'],
42709                     data : [] 
42710                 }),
42711                 name : '-roo-edit-className',
42712                 attrname : 'className',
42713                 displayField: 'val',
42714                 typeAhead: false,
42715                 mode: 'local',
42716                 editable : false,
42717                 triggerAction: 'all',
42718                 emptyText:'Select Style',
42719                 selectOnFocus:true,
42720                 width: 130,
42721                 listeners : {
42722                     'select': function(c, r, i) {
42723                         // initial support only for on class per el..
42724                         tb.selectedNode.className =  r ? r.get('val') : '';
42725                         editor.syncValue();
42726                     }
42727                 }
42728     
42729             }));
42730         }
42731         
42732         var tbc = Roo.form.HtmlEditor.ToolbarContext;
42733         var tbops = tbc.options;
42734         
42735         for (var i in tlist) {
42736             
42737             var item = tlist[i];
42738             tb.add(item.title + ":&nbsp;");
42739             
42740             
42741             //optname == used so you can configure the options available..
42742             var opts = item.opts ? item.opts : false;
42743             if (item.optname) {
42744                 opts = tbops[item.optname];
42745            
42746             }
42747             
42748             if (opts) {
42749                 // opts == pulldown..
42750                 tb.addField( new Roo.form.ComboBox({
42751                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
42752                         id : 'val',
42753                         fields: ['val', 'display'],
42754                         data : opts  
42755                     }),
42756                     name : '-roo-edit-' + i,
42757                     attrname : i,
42758                     stylename : item.style ? item.style : false,
42759                     displayField: item.displayField ? item.displayField : 'val',
42760                     valueField :  'val',
42761                     typeAhead: false,
42762                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
42763                     editable : false,
42764                     triggerAction: 'all',
42765                     emptyText:'Select',
42766                     selectOnFocus:true,
42767                     width: item.width ? item.width  : 130,
42768                     listeners : {
42769                         'select': function(c, r, i) {
42770                             if (c.stylename) {
42771                                 tb.selectedNode.style[c.stylename] =  r.get('val');
42772                                 return;
42773                             }
42774                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
42775                         }
42776                     }
42777
42778                 }));
42779                 continue;
42780                     
42781                  
42782                 
42783                 tb.addField( new Roo.form.TextField({
42784                     name: i,
42785                     width: 100,
42786                     //allowBlank:false,
42787                     value: ''
42788                 }));
42789                 continue;
42790             }
42791             tb.addField( new Roo.form.TextField({
42792                 name: '-roo-edit-' + i,
42793                 attrname : i,
42794                 
42795                 width: item.width,
42796                 //allowBlank:true,
42797                 value: '',
42798                 listeners: {
42799                     'change' : function(f, nv, ov) {
42800                         tb.selectedNode.setAttribute(f.attrname, nv);
42801                     }
42802                 }
42803             }));
42804              
42805         }
42806         tb.addFill();
42807         var _this = this;
42808         tb.addButton( {
42809             text: 'Remove Tag',
42810     
42811             listeners : {
42812                 click : function ()
42813                 {
42814                     // remove
42815                     // undo does not work.
42816                      
42817                     var sn = tb.selectedNode;
42818                     
42819                     var pn = sn.parentNode;
42820                     
42821                     var stn =  sn.childNodes[0];
42822                     var en = sn.childNodes[sn.childNodes.length - 1 ];
42823                     while (sn.childNodes.length) {
42824                         var node = sn.childNodes[0];
42825                         sn.removeChild(node);
42826                         //Roo.log(node);
42827                         pn.insertBefore(node, sn);
42828                         
42829                     }
42830                     pn.removeChild(sn);
42831                     var range = editor.createRange();
42832         
42833                     range.setStart(stn,0);
42834                     range.setEnd(en,0); //????
42835                     //range.selectNode(sel);
42836                     
42837                     
42838                     var selection = editor.getSelection();
42839                     selection.removeAllRanges();
42840                     selection.addRange(range);
42841                     
42842                     
42843                     
42844                     //_this.updateToolbar(null, null, pn);
42845                     _this.updateToolbar(null, null, null);
42846                     _this.footDisp.dom.innerHTML = ''; 
42847                 }
42848             }
42849             
42850                     
42851                 
42852             
42853         });
42854         
42855         
42856         tb.el.on('click', function(e){
42857             e.preventDefault(); // what does this do?
42858         });
42859         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
42860         tb.el.hide();
42861         tb.name = nm;
42862         // dont need to disable them... as they will get hidden
42863         return tb;
42864          
42865         
42866     },
42867     buildFooter : function()
42868     {
42869         
42870         var fel = this.editor.wrap.createChild();
42871         this.footer = new Roo.Toolbar(fel);
42872         // toolbar has scrolly on left / right?
42873         var footDisp= new Roo.Toolbar.Fill();
42874         var _t = this;
42875         this.footer.add(
42876             {
42877                 text : '&lt;',
42878                 xtype: 'Button',
42879                 handler : function() {
42880                     _t.footDisp.scrollTo('left',0,true)
42881                 }
42882             }
42883         );
42884         this.footer.add( footDisp );
42885         this.footer.add( 
42886             {
42887                 text : '&gt;',
42888                 xtype: 'Button',
42889                 handler : function() {
42890                     // no animation..
42891                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
42892                 }
42893             }
42894         );
42895         var fel = Roo.get(footDisp.el);
42896         fel.addClass('x-editor-context');
42897         this.footDispWrap = fel; 
42898         this.footDispWrap.overflow  = 'hidden';
42899         
42900         this.footDisp = fel.createChild();
42901         this.footDispWrap.on('click', this.onContextClick, this)
42902         
42903         
42904     },
42905     onContextClick : function (ev,dom)
42906     {
42907         ev.preventDefault();
42908         var  cn = dom.className;
42909         //Roo.log(cn);
42910         if (!cn.match(/x-ed-loc-/)) {
42911             return;
42912         }
42913         var n = cn.split('-').pop();
42914         var ans = this.footerEls;
42915         var sel = ans[n];
42916         
42917          // pick
42918         var range = this.editor.createRange();
42919         
42920         range.selectNodeContents(sel);
42921         //range.selectNode(sel);
42922         
42923         
42924         var selection = this.editor.getSelection();
42925         selection.removeAllRanges();
42926         selection.addRange(range);
42927         
42928         
42929         
42930         this.updateToolbar(null, null, sel);
42931         
42932         
42933     }
42934     
42935     
42936     
42937     
42938     
42939 });
42940
42941
42942
42943
42944
42945 /*
42946  * Based on:
42947  * Ext JS Library 1.1.1
42948  * Copyright(c) 2006-2007, Ext JS, LLC.
42949  *
42950  * Originally Released Under LGPL - original licence link has changed is not relivant.
42951  *
42952  * Fork - LGPL
42953  * <script type="text/javascript">
42954  */
42955  
42956 /**
42957  * @class Roo.form.BasicForm
42958  * @extends Roo.util.Observable
42959  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
42960  * @constructor
42961  * @param {String/HTMLElement/Roo.Element} el The form element or its id
42962  * @param {Object} config Configuration options
42963  */
42964 Roo.form.BasicForm = function(el, config){
42965     this.allItems = [];
42966     this.childForms = [];
42967     Roo.apply(this, config);
42968     /*
42969      * The Roo.form.Field items in this form.
42970      * @type MixedCollection
42971      */
42972      
42973      
42974     this.items = new Roo.util.MixedCollection(false, function(o){
42975         return o.id || (o.id = Roo.id());
42976     });
42977     this.addEvents({
42978         /**
42979          * @event beforeaction
42980          * Fires before any action is performed. Return false to cancel the action.
42981          * @param {Form} this
42982          * @param {Action} action The action to be performed
42983          */
42984         beforeaction: true,
42985         /**
42986          * @event actionfailed
42987          * Fires when an action fails.
42988          * @param {Form} this
42989          * @param {Action} action The action that failed
42990          */
42991         actionfailed : true,
42992         /**
42993          * @event actioncomplete
42994          * Fires when an action is completed.
42995          * @param {Form} this
42996          * @param {Action} action The action that completed
42997          */
42998         actioncomplete : true
42999     });
43000     if(el){
43001         this.initEl(el);
43002     }
43003     Roo.form.BasicForm.superclass.constructor.call(this);
43004 };
43005
43006 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
43007     /**
43008      * @cfg {String} method
43009      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
43010      */
43011     /**
43012      * @cfg {DataReader} reader
43013      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
43014      * This is optional as there is built-in support for processing JSON.
43015      */
43016     /**
43017      * @cfg {DataReader} errorReader
43018      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
43019      * This is completely optional as there is built-in support for processing JSON.
43020      */
43021     /**
43022      * @cfg {String} url
43023      * The URL to use for form actions if one isn't supplied in the action options.
43024      */
43025     /**
43026      * @cfg {Boolean} fileUpload
43027      * Set to true if this form is a file upload.
43028      */
43029      
43030     /**
43031      * @cfg {Object} baseParams
43032      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
43033      */
43034      /**
43035      
43036     /**
43037      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
43038      */
43039     timeout: 30,
43040
43041     // private
43042     activeAction : null,
43043
43044     /**
43045      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
43046      * or setValues() data instead of when the form was first created.
43047      */
43048     trackResetOnLoad : false,
43049     
43050     
43051     /**
43052      * childForms - used for multi-tab forms
43053      * @type {Array}
43054      */
43055     childForms : false,
43056     
43057     /**
43058      * allItems - full list of fields.
43059      * @type {Array}
43060      */
43061     allItems : false,
43062     
43063     /**
43064      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
43065      * element by passing it or its id or mask the form itself by passing in true.
43066      * @type Mixed
43067      */
43068     waitMsgTarget : false,
43069
43070     // private
43071     initEl : function(el){
43072         this.el = Roo.get(el);
43073         this.id = this.el.id || Roo.id();
43074         this.el.on('submit', this.onSubmit, this);
43075         this.el.addClass('x-form');
43076     },
43077
43078     // private
43079     onSubmit : function(e){
43080         e.stopEvent();
43081     },
43082
43083     /**
43084      * Returns true if client-side validation on the form is successful.
43085      * @return Boolean
43086      */
43087     isValid : function(){
43088         var valid = true;
43089         this.items.each(function(f){
43090            if(!f.validate()){
43091                valid = false;
43092            }
43093         });
43094         return valid;
43095     },
43096
43097     /**
43098      * Returns true if any fields in this form have changed since their original load.
43099      * @return Boolean
43100      */
43101     isDirty : function(){
43102         var dirty = false;
43103         this.items.each(function(f){
43104            if(f.isDirty()){
43105                dirty = true;
43106                return false;
43107            }
43108         });
43109         return dirty;
43110     },
43111
43112     /**
43113      * Performs a predefined action (submit or load) or custom actions you define on this form.
43114      * @param {String} actionName The name of the action type
43115      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
43116      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
43117      * accept other config options):
43118      * <pre>
43119 Property          Type             Description
43120 ----------------  ---------------  ----------------------------------------------------------------------------------
43121 url               String           The url for the action (defaults to the form's url)
43122 method            String           The form method to use (defaults to the form's method, or POST if not defined)
43123 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
43124 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
43125                                    validate the form on the client (defaults to false)
43126      * </pre>
43127      * @return {BasicForm} this
43128      */
43129     doAction : function(action, options){
43130         if(typeof action == 'string'){
43131             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
43132         }
43133         if(this.fireEvent('beforeaction', this, action) !== false){
43134             this.beforeAction(action);
43135             action.run.defer(100, action);
43136         }
43137         return this;
43138     },
43139
43140     /**
43141      * Shortcut to do a submit action.
43142      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43143      * @return {BasicForm} this
43144      */
43145     submit : function(options){
43146         this.doAction('submit', options);
43147         return this;
43148     },
43149
43150     /**
43151      * Shortcut to do a load action.
43152      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43153      * @return {BasicForm} this
43154      */
43155     load : function(options){
43156         this.doAction('load', options);
43157         return this;
43158     },
43159
43160     /**
43161      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
43162      * @param {Record} record The record to edit
43163      * @return {BasicForm} this
43164      */
43165     updateRecord : function(record){
43166         record.beginEdit();
43167         var fs = record.fields;
43168         fs.each(function(f){
43169             var field = this.findField(f.name);
43170             if(field){
43171                 record.set(f.name, field.getValue());
43172             }
43173         }, this);
43174         record.endEdit();
43175         return this;
43176     },
43177
43178     /**
43179      * Loads an Roo.data.Record into this form.
43180      * @param {Record} record The record to load
43181      * @return {BasicForm} this
43182      */
43183     loadRecord : function(record){
43184         this.setValues(record.data);
43185         return this;
43186     },
43187
43188     // private
43189     beforeAction : function(action){
43190         var o = action.options;
43191         
43192        
43193         if(this.waitMsgTarget === true){
43194             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
43195         }else if(this.waitMsgTarget){
43196             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
43197             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
43198         }else {
43199             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
43200         }
43201          
43202     },
43203
43204     // private
43205     afterAction : function(action, success){
43206         this.activeAction = null;
43207         var o = action.options;
43208         
43209         if(this.waitMsgTarget === true){
43210             this.el.unmask();
43211         }else if(this.waitMsgTarget){
43212             this.waitMsgTarget.unmask();
43213         }else{
43214             Roo.MessageBox.updateProgress(1);
43215             Roo.MessageBox.hide();
43216         }
43217          
43218         if(success){
43219             if(o.reset){
43220                 this.reset();
43221             }
43222             Roo.callback(o.success, o.scope, [this, action]);
43223             this.fireEvent('actioncomplete', this, action);
43224             
43225         }else{
43226             
43227             // failure condition..
43228             // we have a scenario where updates need confirming.
43229             // eg. if a locking scenario exists..
43230             // we look for { errors : { needs_confirm : true }} in the response.
43231             if (
43232                 (typeof(action.result) != 'undefined')  &&
43233                 (typeof(action.result.errors) != 'undefined')  &&
43234                 (typeof(action.result.errors.needs_confirm) != 'undefined')
43235            ){
43236                 var _t = this;
43237                 Roo.MessageBox.confirm(
43238                     "Change requires confirmation",
43239                     action.result.errorMsg,
43240                     function(r) {
43241                         if (r != 'yes') {
43242                             return;
43243                         }
43244                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
43245                     }
43246                     
43247                 );
43248                 
43249                 
43250                 
43251                 return;
43252             }
43253             
43254             Roo.callback(o.failure, o.scope, [this, action]);
43255             // show an error message if no failed handler is set..
43256             if (!this.hasListener('actionfailed')) {
43257                 Roo.MessageBox.alert("Error",
43258                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
43259                         action.result.errorMsg :
43260                         "Saving Failed, please check your entries or try again"
43261                 );
43262             }
43263             
43264             this.fireEvent('actionfailed', this, action);
43265         }
43266         
43267     },
43268
43269     /**
43270      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
43271      * @param {String} id The value to search for
43272      * @return Field
43273      */
43274     findField : function(id){
43275         var field = this.items.get(id);
43276         if(!field){
43277             this.items.each(function(f){
43278                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
43279                     field = f;
43280                     return false;
43281                 }
43282             });
43283         }
43284         return field || null;
43285     },
43286
43287     /**
43288      * Add a secondary form to this one, 
43289      * Used to provide tabbed forms. One form is primary, with hidden values 
43290      * which mirror the elements from the other forms.
43291      * 
43292      * @param {Roo.form.Form} form to add.
43293      * 
43294      */
43295     addForm : function(form)
43296     {
43297        
43298         if (this.childForms.indexOf(form) > -1) {
43299             // already added..
43300             return;
43301         }
43302         this.childForms.push(form);
43303         var n = '';
43304         Roo.each(form.allItems, function (fe) {
43305             
43306             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
43307             if (this.findField(n)) { // already added..
43308                 return;
43309             }
43310             var add = new Roo.form.Hidden({
43311                 name : n
43312             });
43313             add.render(this.el);
43314             
43315             this.add( add );
43316         }, this);
43317         
43318     },
43319     /**
43320      * Mark fields in this form invalid in bulk.
43321      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
43322      * @return {BasicForm} this
43323      */
43324     markInvalid : function(errors){
43325         if(errors instanceof Array){
43326             for(var i = 0, len = errors.length; i < len; i++){
43327                 var fieldError = errors[i];
43328                 var f = this.findField(fieldError.id);
43329                 if(f){
43330                     f.markInvalid(fieldError.msg);
43331                 }
43332             }
43333         }else{
43334             var field, id;
43335             for(id in errors){
43336                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
43337                     field.markInvalid(errors[id]);
43338                 }
43339             }
43340         }
43341         Roo.each(this.childForms || [], function (f) {
43342             f.markInvalid(errors);
43343         });
43344         
43345         return this;
43346     },
43347
43348     /**
43349      * Set values for fields in this form in bulk.
43350      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
43351      * @return {BasicForm} this
43352      */
43353     setValues : function(values){
43354         if(values instanceof Array){ // array of objects
43355             for(var i = 0, len = values.length; i < len; i++){
43356                 var v = values[i];
43357                 var f = this.findField(v.id);
43358                 if(f){
43359                     f.setValue(v.value);
43360                     if(this.trackResetOnLoad){
43361                         f.originalValue = f.getValue();
43362                     }
43363                 }
43364             }
43365         }else{ // object hash
43366             var field, id;
43367             for(id in values){
43368                 if(typeof values[id] != 'function' && (field = this.findField(id))){
43369                     
43370                     if (field.setFromData && 
43371                         field.valueField && 
43372                         field.displayField &&
43373                         // combos' with local stores can 
43374                         // be queried via setValue()
43375                         // to set their value..
43376                         (field.store && !field.store.isLocal)
43377                         ) {
43378                         // it's a combo
43379                         var sd = { };
43380                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
43381                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
43382                         field.setFromData(sd);
43383                         
43384                     } else {
43385                         field.setValue(values[id]);
43386                     }
43387                     
43388                     
43389                     if(this.trackResetOnLoad){
43390                         field.originalValue = field.getValue();
43391                     }
43392                 }
43393             }
43394         }
43395          
43396         Roo.each(this.childForms || [], function (f) {
43397             f.setValues(values);
43398         });
43399                 
43400         return this;
43401     },
43402
43403     /**
43404      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
43405      * they are returned as an array.
43406      * @param {Boolean} asString
43407      * @return {Object}
43408      */
43409     getValues : function(asString){
43410         if (this.childForms) {
43411             // copy values from the child forms
43412             Roo.each(this.childForms, function (f) {
43413                 this.setValues(f.getValues());
43414             }, this);
43415         }
43416         
43417         
43418         
43419         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
43420         if(asString === true){
43421             return fs;
43422         }
43423         return Roo.urlDecode(fs);
43424     },
43425     
43426     /**
43427      * Returns the fields in this form as an object with key/value pairs. 
43428      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
43429      * @return {Object}
43430      */
43431     getFieldValues : function(with_hidden)
43432     {
43433         if (this.childForms) {
43434             // copy values from the child forms
43435             // should this call getFieldValues - probably not as we do not currently copy
43436             // hidden fields when we generate..
43437             Roo.each(this.childForms, function (f) {
43438                 this.setValues(f.getValues());
43439             }, this);
43440         }
43441         
43442         var ret = {};
43443         this.items.each(function(f){
43444             if (!f.getName()) {
43445                 return;
43446             }
43447             var v = f.getValue();
43448             if (f.inputType =='radio') {
43449                 if (typeof(ret[f.getName()]) == 'undefined') {
43450                     ret[f.getName()] = ''; // empty..
43451                 }
43452                 
43453                 if (!f.el.dom.checked) {
43454                     return;
43455                     
43456                 }
43457                 v = f.el.dom.value;
43458                 
43459             }
43460             
43461             // not sure if this supported any more..
43462             if ((typeof(v) == 'object') && f.getRawValue) {
43463                 v = f.getRawValue() ; // dates..
43464             }
43465             // combo boxes where name != hiddenName...
43466             if (f.name != f.getName()) {
43467                 ret[f.name] = f.getRawValue();
43468             }
43469             ret[f.getName()] = v;
43470         });
43471         
43472         return ret;
43473     },
43474
43475     /**
43476      * Clears all invalid messages in this form.
43477      * @return {BasicForm} this
43478      */
43479     clearInvalid : function(){
43480         this.items.each(function(f){
43481            f.clearInvalid();
43482         });
43483         
43484         Roo.each(this.childForms || [], function (f) {
43485             f.clearInvalid();
43486         });
43487         
43488         
43489         return this;
43490     },
43491
43492     /**
43493      * Resets this form.
43494      * @return {BasicForm} this
43495      */
43496     reset : function(){
43497         this.items.each(function(f){
43498             f.reset();
43499         });
43500         
43501         Roo.each(this.childForms || [], function (f) {
43502             f.reset();
43503         });
43504        
43505         
43506         return this;
43507     },
43508
43509     /**
43510      * Add Roo.form components to this form.
43511      * @param {Field} field1
43512      * @param {Field} field2 (optional)
43513      * @param {Field} etc (optional)
43514      * @return {BasicForm} this
43515      */
43516     add : function(){
43517         this.items.addAll(Array.prototype.slice.call(arguments, 0));
43518         return this;
43519     },
43520
43521
43522     /**
43523      * Removes a field from the items collection (does NOT remove its markup).
43524      * @param {Field} field
43525      * @return {BasicForm} this
43526      */
43527     remove : function(field){
43528         this.items.remove(field);
43529         return this;
43530     },
43531
43532     /**
43533      * Looks at the fields in this form, checks them for an id attribute,
43534      * and calls applyTo on the existing dom element with that id.
43535      * @return {BasicForm} this
43536      */
43537     render : function(){
43538         this.items.each(function(f){
43539             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
43540                 f.applyTo(f.id);
43541             }
43542         });
43543         return this;
43544     },
43545
43546     /**
43547      * Calls {@link Ext#apply} for all fields in this form with the passed object.
43548      * @param {Object} values
43549      * @return {BasicForm} this
43550      */
43551     applyToFields : function(o){
43552         this.items.each(function(f){
43553            Roo.apply(f, o);
43554         });
43555         return this;
43556     },
43557
43558     /**
43559      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
43560      * @param {Object} values
43561      * @return {BasicForm} this
43562      */
43563     applyIfToFields : function(o){
43564         this.items.each(function(f){
43565            Roo.applyIf(f, o);
43566         });
43567         return this;
43568     }
43569 });
43570
43571 // back compat
43572 Roo.BasicForm = Roo.form.BasicForm;/*
43573  * Based on:
43574  * Ext JS Library 1.1.1
43575  * Copyright(c) 2006-2007, Ext JS, LLC.
43576  *
43577  * Originally Released Under LGPL - original licence link has changed is not relivant.
43578  *
43579  * Fork - LGPL
43580  * <script type="text/javascript">
43581  */
43582
43583 /**
43584  * @class Roo.form.Form
43585  * @extends Roo.form.BasicForm
43586  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
43587  * @constructor
43588  * @param {Object} config Configuration options
43589  */
43590 Roo.form.Form = function(config){
43591     var xitems =  [];
43592     if (config.items) {
43593         xitems = config.items;
43594         delete config.items;
43595     }
43596    
43597     
43598     Roo.form.Form.superclass.constructor.call(this, null, config);
43599     this.url = this.url || this.action;
43600     if(!this.root){
43601         this.root = new Roo.form.Layout(Roo.applyIf({
43602             id: Roo.id()
43603         }, config));
43604     }
43605     this.active = this.root;
43606     /**
43607      * Array of all the buttons that have been added to this form via {@link addButton}
43608      * @type Array
43609      */
43610     this.buttons = [];
43611     this.allItems = [];
43612     this.addEvents({
43613         /**
43614          * @event clientvalidation
43615          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
43616          * @param {Form} this
43617          * @param {Boolean} valid true if the form has passed client-side validation
43618          */
43619         clientvalidation: true,
43620         /**
43621          * @event rendered
43622          * Fires when the form is rendered
43623          * @param {Roo.form.Form} form
43624          */
43625         rendered : true
43626     });
43627     
43628     if (this.progressUrl) {
43629             // push a hidden field onto the list of fields..
43630             this.addxtype( {
43631                     xns: Roo.form, 
43632                     xtype : 'Hidden', 
43633                     name : 'UPLOAD_IDENTIFIER' 
43634             });
43635         }
43636         
43637     
43638     Roo.each(xitems, this.addxtype, this);
43639     
43640     
43641     
43642 };
43643
43644 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
43645     /**
43646      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
43647      */
43648     /**
43649      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
43650      */
43651     /**
43652      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
43653      */
43654     buttonAlign:'center',
43655
43656     /**
43657      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
43658      */
43659     minButtonWidth:75,
43660
43661     /**
43662      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
43663      * This property cascades to child containers if not set.
43664      */
43665     labelAlign:'left',
43666
43667     /**
43668      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
43669      * fires a looping event with that state. This is required to bind buttons to the valid
43670      * state using the config value formBind:true on the button.
43671      */
43672     monitorValid : false,
43673
43674     /**
43675      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
43676      */
43677     monitorPoll : 200,
43678     
43679     /**
43680      * @cfg {String} progressUrl - Url to return progress data 
43681      */
43682     
43683     progressUrl : false,
43684   
43685     /**
43686      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
43687      * fields are added and the column is closed. If no fields are passed the column remains open
43688      * until end() is called.
43689      * @param {Object} config The config to pass to the column
43690      * @param {Field} field1 (optional)
43691      * @param {Field} field2 (optional)
43692      * @param {Field} etc (optional)
43693      * @return Column The column container object
43694      */
43695     column : function(c){
43696         var col = new Roo.form.Column(c);
43697         this.start(col);
43698         if(arguments.length > 1){ // duplicate code required because of Opera
43699             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43700             this.end();
43701         }
43702         return col;
43703     },
43704
43705     /**
43706      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
43707      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
43708      * until end() is called.
43709      * @param {Object} config The config to pass to the fieldset
43710      * @param {Field} field1 (optional)
43711      * @param {Field} field2 (optional)
43712      * @param {Field} etc (optional)
43713      * @return FieldSet The fieldset container object
43714      */
43715     fieldset : function(c){
43716         var fs = new Roo.form.FieldSet(c);
43717         this.start(fs);
43718         if(arguments.length > 1){ // duplicate code required because of Opera
43719             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43720             this.end();
43721         }
43722         return fs;
43723     },
43724
43725     /**
43726      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
43727      * fields are added and the container is closed. If no fields are passed the container remains open
43728      * until end() is called.
43729      * @param {Object} config The config to pass to the Layout
43730      * @param {Field} field1 (optional)
43731      * @param {Field} field2 (optional)
43732      * @param {Field} etc (optional)
43733      * @return Layout The container object
43734      */
43735     container : function(c){
43736         var l = new Roo.form.Layout(c);
43737         this.start(l);
43738         if(arguments.length > 1){ // duplicate code required because of Opera
43739             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43740             this.end();
43741         }
43742         return l;
43743     },
43744
43745     /**
43746      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
43747      * @param {Object} container A Roo.form.Layout or subclass of Layout
43748      * @return {Form} this
43749      */
43750     start : function(c){
43751         // cascade label info
43752         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
43753         this.active.stack.push(c);
43754         c.ownerCt = this.active;
43755         this.active = c;
43756         return this;
43757     },
43758
43759     /**
43760      * Closes the current open container
43761      * @return {Form} this
43762      */
43763     end : function(){
43764         if(this.active == this.root){
43765             return this;
43766         }
43767         this.active = this.active.ownerCt;
43768         return this;
43769     },
43770
43771     /**
43772      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
43773      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
43774      * as the label of the field.
43775      * @param {Field} field1
43776      * @param {Field} field2 (optional)
43777      * @param {Field} etc. (optional)
43778      * @return {Form} this
43779      */
43780     add : function(){
43781         this.active.stack.push.apply(this.active.stack, arguments);
43782         this.allItems.push.apply(this.allItems,arguments);
43783         var r = [];
43784         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
43785             if(a[i].isFormField){
43786                 r.push(a[i]);
43787             }
43788         }
43789         if(r.length > 0){
43790             Roo.form.Form.superclass.add.apply(this, r);
43791         }
43792         return this;
43793     },
43794     
43795
43796     
43797     
43798     
43799      /**
43800      * Find any element that has been added to a form, using it's ID or name
43801      * This can include framesets, columns etc. along with regular fields..
43802      * @param {String} id - id or name to find.
43803      
43804      * @return {Element} e - or false if nothing found.
43805      */
43806     findbyId : function(id)
43807     {
43808         var ret = false;
43809         if (!id) {
43810             return ret;
43811         }
43812         Roo.each(this.allItems, function(f){
43813             if (f.id == id || f.name == id ){
43814                 ret = f;
43815                 return false;
43816             }
43817         });
43818         return ret;
43819     },
43820
43821     
43822     
43823     /**
43824      * Render this form into the passed container. This should only be called once!
43825      * @param {String/HTMLElement/Element} container The element this component should be rendered into
43826      * @return {Form} this
43827      */
43828     render : function(ct)
43829     {
43830         
43831         
43832         
43833         ct = Roo.get(ct);
43834         var o = this.autoCreate || {
43835             tag: 'form',
43836             method : this.method || 'POST',
43837             id : this.id || Roo.id()
43838         };
43839         this.initEl(ct.createChild(o));
43840
43841         this.root.render(this.el);
43842         
43843        
43844              
43845         this.items.each(function(f){
43846             f.render('x-form-el-'+f.id);
43847         });
43848
43849         if(this.buttons.length > 0){
43850             // tables are required to maintain order and for correct IE layout
43851             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
43852                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
43853                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
43854             }}, null, true);
43855             var tr = tb.getElementsByTagName('tr')[0];
43856             for(var i = 0, len = this.buttons.length; i < len; i++) {
43857                 var b = this.buttons[i];
43858                 var td = document.createElement('td');
43859                 td.className = 'x-form-btn-td';
43860                 b.render(tr.appendChild(td));
43861             }
43862         }
43863         if(this.monitorValid){ // initialize after render
43864             this.startMonitoring();
43865         }
43866         this.fireEvent('rendered', this);
43867         return this;
43868     },
43869
43870     /**
43871      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
43872      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
43873      * object or a valid Roo.DomHelper element config
43874      * @param {Function} handler The function called when the button is clicked
43875      * @param {Object} scope (optional) The scope of the handler function
43876      * @return {Roo.Button}
43877      */
43878     addButton : function(config, handler, scope){
43879         var bc = {
43880             handler: handler,
43881             scope: scope,
43882             minWidth: this.minButtonWidth,
43883             hideParent:true
43884         };
43885         if(typeof config == "string"){
43886             bc.text = config;
43887         }else{
43888             Roo.apply(bc, config);
43889         }
43890         var btn = new Roo.Button(null, bc);
43891         this.buttons.push(btn);
43892         return btn;
43893     },
43894
43895      /**
43896      * Adds a series of form elements (using the xtype property as the factory method.
43897      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
43898      * @param {Object} config 
43899      */
43900     
43901     addxtype : function()
43902     {
43903         var ar = Array.prototype.slice.call(arguments, 0);
43904         var ret = false;
43905         for(var i = 0; i < ar.length; i++) {
43906             if (!ar[i]) {
43907                 continue; // skip -- if this happends something invalid got sent, we 
43908                 // should ignore it, as basically that interface element will not show up
43909                 // and that should be pretty obvious!!
43910             }
43911             
43912             if (Roo.form[ar[i].xtype]) {
43913                 ar[i].form = this;
43914                 var fe = Roo.factory(ar[i], Roo.form);
43915                 if (!ret) {
43916                     ret = fe;
43917                 }
43918                 fe.form = this;
43919                 if (fe.store) {
43920                     fe.store.form = this;
43921                 }
43922                 if (fe.isLayout) {  
43923                          
43924                     this.start(fe);
43925                     this.allItems.push(fe);
43926                     if (fe.items && fe.addxtype) {
43927                         fe.addxtype.apply(fe, fe.items);
43928                         delete fe.items;
43929                     }
43930                      this.end();
43931                     continue;
43932                 }
43933                 
43934                 
43935                  
43936                 this.add(fe);
43937               //  console.log('adding ' + ar[i].xtype);
43938             }
43939             if (ar[i].xtype == 'Button') {  
43940                 //console.log('adding button');
43941                 //console.log(ar[i]);
43942                 this.addButton(ar[i]);
43943                 this.allItems.push(fe);
43944                 continue;
43945             }
43946             
43947             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
43948                 alert('end is not supported on xtype any more, use items');
43949             //    this.end();
43950             //    //console.log('adding end');
43951             }
43952             
43953         }
43954         return ret;
43955     },
43956     
43957     /**
43958      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
43959      * option "monitorValid"
43960      */
43961     startMonitoring : function(){
43962         if(!this.bound){
43963             this.bound = true;
43964             Roo.TaskMgr.start({
43965                 run : this.bindHandler,
43966                 interval : this.monitorPoll || 200,
43967                 scope: this
43968             });
43969         }
43970     },
43971
43972     /**
43973      * Stops monitoring of the valid state of this form
43974      */
43975     stopMonitoring : function(){
43976         this.bound = false;
43977     },
43978
43979     // private
43980     bindHandler : function(){
43981         if(!this.bound){
43982             return false; // stops binding
43983         }
43984         var valid = true;
43985         this.items.each(function(f){
43986             if(!f.isValid(true)){
43987                 valid = false;
43988                 return false;
43989             }
43990         });
43991         for(var i = 0, len = this.buttons.length; i < len; i++){
43992             var btn = this.buttons[i];
43993             if(btn.formBind === true && btn.disabled === valid){
43994                 btn.setDisabled(!valid);
43995             }
43996         }
43997         this.fireEvent('clientvalidation', this, valid);
43998     }
43999     
44000     
44001     
44002     
44003     
44004     
44005     
44006     
44007 });
44008
44009
44010 // back compat
44011 Roo.Form = Roo.form.Form;
44012 /*
44013  * Based on:
44014  * Ext JS Library 1.1.1
44015  * Copyright(c) 2006-2007, Ext JS, LLC.
44016  *
44017  * Originally Released Under LGPL - original licence link has changed is not relivant.
44018  *
44019  * Fork - LGPL
44020  * <script type="text/javascript">
44021  */
44022  
44023  /**
44024  * @class Roo.form.Action
44025  * Internal Class used to handle form actions
44026  * @constructor
44027  * @param {Roo.form.BasicForm} el The form element or its id
44028  * @param {Object} config Configuration options
44029  */
44030  
44031  
44032 // define the action interface
44033 Roo.form.Action = function(form, options){
44034     this.form = form;
44035     this.options = options || {};
44036 };
44037 /**
44038  * Client Validation Failed
44039  * @const 
44040  */
44041 Roo.form.Action.CLIENT_INVALID = 'client';
44042 /**
44043  * Server Validation Failed
44044  * @const 
44045  */
44046  Roo.form.Action.SERVER_INVALID = 'server';
44047  /**
44048  * Connect to Server Failed
44049  * @const 
44050  */
44051 Roo.form.Action.CONNECT_FAILURE = 'connect';
44052 /**
44053  * Reading Data from Server Failed
44054  * @const 
44055  */
44056 Roo.form.Action.LOAD_FAILURE = 'load';
44057
44058 Roo.form.Action.prototype = {
44059     type : 'default',
44060     failureType : undefined,
44061     response : undefined,
44062     result : undefined,
44063
44064     // interface method
44065     run : function(options){
44066
44067     },
44068
44069     // interface method
44070     success : function(response){
44071
44072     },
44073
44074     // interface method
44075     handleResponse : function(response){
44076
44077     },
44078
44079     // default connection failure
44080     failure : function(response){
44081         
44082         this.response = response;
44083         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44084         this.form.afterAction(this, false);
44085     },
44086
44087     processResponse : function(response){
44088         this.response = response;
44089         if(!response.responseText){
44090             return true;
44091         }
44092         this.result = this.handleResponse(response);
44093         return this.result;
44094     },
44095
44096     // utility functions used internally
44097     getUrl : function(appendParams){
44098         var url = this.options.url || this.form.url || this.form.el.dom.action;
44099         if(appendParams){
44100             var p = this.getParams();
44101             if(p){
44102                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
44103             }
44104         }
44105         return url;
44106     },
44107
44108     getMethod : function(){
44109         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
44110     },
44111
44112     getParams : function(){
44113         var bp = this.form.baseParams;
44114         var p = this.options.params;
44115         if(p){
44116             if(typeof p == "object"){
44117                 p = Roo.urlEncode(Roo.applyIf(p, bp));
44118             }else if(typeof p == 'string' && bp){
44119                 p += '&' + Roo.urlEncode(bp);
44120             }
44121         }else if(bp){
44122             p = Roo.urlEncode(bp);
44123         }
44124         return p;
44125     },
44126
44127     createCallback : function(){
44128         return {
44129             success: this.success,
44130             failure: this.failure,
44131             scope: this,
44132             timeout: (this.form.timeout*1000),
44133             upload: this.form.fileUpload ? this.success : undefined
44134         };
44135     }
44136 };
44137
44138 Roo.form.Action.Submit = function(form, options){
44139     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
44140 };
44141
44142 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
44143     type : 'submit',
44144
44145     haveProgress : false,
44146     uploadComplete : false,
44147     
44148     // uploadProgress indicator.
44149     uploadProgress : function()
44150     {
44151         if (!this.form.progressUrl) {
44152             return;
44153         }
44154         
44155         if (!this.haveProgress) {
44156             Roo.MessageBox.progress("Uploading", "Uploading");
44157         }
44158         if (this.uploadComplete) {
44159            Roo.MessageBox.hide();
44160            return;
44161         }
44162         
44163         this.haveProgress = true;
44164    
44165         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
44166         
44167         var c = new Roo.data.Connection();
44168         c.request({
44169             url : this.form.progressUrl,
44170             params: {
44171                 id : uid
44172             },
44173             method: 'GET',
44174             success : function(req){
44175                //console.log(data);
44176                 var rdata = false;
44177                 var edata;
44178                 try  {
44179                    rdata = Roo.decode(req.responseText)
44180                 } catch (e) {
44181                     Roo.log("Invalid data from server..");
44182                     Roo.log(edata);
44183                     return;
44184                 }
44185                 if (!rdata || !rdata.success) {
44186                     Roo.log(rdata);
44187                     Roo.MessageBox.alert(Roo.encode(rdata));
44188                     return;
44189                 }
44190                 var data = rdata.data;
44191                 
44192                 if (this.uploadComplete) {
44193                    Roo.MessageBox.hide();
44194                    return;
44195                 }
44196                    
44197                 if (data){
44198                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
44199                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
44200                     );
44201                 }
44202                 this.uploadProgress.defer(2000,this);
44203             },
44204        
44205             failure: function(data) {
44206                 Roo.log('progress url failed ');
44207                 Roo.log(data);
44208             },
44209             scope : this
44210         });
44211            
44212     },
44213     
44214     
44215     run : function()
44216     {
44217         // run get Values on the form, so it syncs any secondary forms.
44218         this.form.getValues();
44219         
44220         var o = this.options;
44221         var method = this.getMethod();
44222         var isPost = method == 'POST';
44223         if(o.clientValidation === false || this.form.isValid()){
44224             
44225             if (this.form.progressUrl) {
44226                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
44227                     (new Date() * 1) + '' + Math.random());
44228                     
44229             } 
44230             
44231             
44232             Roo.Ajax.request(Roo.apply(this.createCallback(), {
44233                 form:this.form.el.dom,
44234                 url:this.getUrl(!isPost),
44235                 method: method,
44236                 params:isPost ? this.getParams() : null,
44237                 isUpload: this.form.fileUpload
44238             }));
44239             
44240             this.uploadProgress();
44241
44242         }else if (o.clientValidation !== false){ // client validation failed
44243             this.failureType = Roo.form.Action.CLIENT_INVALID;
44244             this.form.afterAction(this, false);
44245         }
44246     },
44247
44248     success : function(response)
44249     {
44250         this.uploadComplete= true;
44251         if (this.haveProgress) {
44252             Roo.MessageBox.hide();
44253         }
44254         
44255         
44256         var result = this.processResponse(response);
44257         if(result === true || result.success){
44258             this.form.afterAction(this, true);
44259             return;
44260         }
44261         if(result.errors){
44262             this.form.markInvalid(result.errors);
44263             this.failureType = Roo.form.Action.SERVER_INVALID;
44264         }
44265         this.form.afterAction(this, false);
44266     },
44267     failure : function(response)
44268     {
44269         this.uploadComplete= true;
44270         if (this.haveProgress) {
44271             Roo.MessageBox.hide();
44272         }
44273         
44274         this.response = response;
44275         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44276         this.form.afterAction(this, false);
44277     },
44278     
44279     handleResponse : function(response){
44280         if(this.form.errorReader){
44281             var rs = this.form.errorReader.read(response);
44282             var errors = [];
44283             if(rs.records){
44284                 for(var i = 0, len = rs.records.length; i < len; i++) {
44285                     var r = rs.records[i];
44286                     errors[i] = r.data;
44287                 }
44288             }
44289             if(errors.length < 1){
44290                 errors = null;
44291             }
44292             return {
44293                 success : rs.success,
44294                 errors : errors
44295             };
44296         }
44297         var ret = false;
44298         try {
44299             ret = Roo.decode(response.responseText);
44300         } catch (e) {
44301             ret = {
44302                 success: false,
44303                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
44304                 errors : []
44305             };
44306         }
44307         return ret;
44308         
44309     }
44310 });
44311
44312
44313 Roo.form.Action.Load = function(form, options){
44314     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
44315     this.reader = this.form.reader;
44316 };
44317
44318 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
44319     type : 'load',
44320
44321     run : function(){
44322         
44323         Roo.Ajax.request(Roo.apply(
44324                 this.createCallback(), {
44325                     method:this.getMethod(),
44326                     url:this.getUrl(false),
44327                     params:this.getParams()
44328         }));
44329     },
44330
44331     success : function(response){
44332         
44333         var result = this.processResponse(response);
44334         if(result === true || !result.success || !result.data){
44335             this.failureType = Roo.form.Action.LOAD_FAILURE;
44336             this.form.afterAction(this, false);
44337             return;
44338         }
44339         this.form.clearInvalid();
44340         this.form.setValues(result.data);
44341         this.form.afterAction(this, true);
44342     },
44343
44344     handleResponse : function(response){
44345         if(this.form.reader){
44346             var rs = this.form.reader.read(response);
44347             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
44348             return {
44349                 success : rs.success,
44350                 data : data
44351             };
44352         }
44353         return Roo.decode(response.responseText);
44354     }
44355 });
44356
44357 Roo.form.Action.ACTION_TYPES = {
44358     'load' : Roo.form.Action.Load,
44359     'submit' : Roo.form.Action.Submit
44360 };/*
44361  * Based on:
44362  * Ext JS Library 1.1.1
44363  * Copyright(c) 2006-2007, Ext JS, LLC.
44364  *
44365  * Originally Released Under LGPL - original licence link has changed is not relivant.
44366  *
44367  * Fork - LGPL
44368  * <script type="text/javascript">
44369  */
44370  
44371 /**
44372  * @class Roo.form.Layout
44373  * @extends Roo.Component
44374  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
44375  * @constructor
44376  * @param {Object} config Configuration options
44377  */
44378 Roo.form.Layout = function(config){
44379     var xitems = [];
44380     if (config.items) {
44381         xitems = config.items;
44382         delete config.items;
44383     }
44384     Roo.form.Layout.superclass.constructor.call(this, config);
44385     this.stack = [];
44386     Roo.each(xitems, this.addxtype, this);
44387      
44388 };
44389
44390 Roo.extend(Roo.form.Layout, Roo.Component, {
44391     /**
44392      * @cfg {String/Object} autoCreate
44393      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
44394      */
44395     /**
44396      * @cfg {String/Object/Function} style
44397      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
44398      * a function which returns such a specification.
44399      */
44400     /**
44401      * @cfg {String} labelAlign
44402      * Valid values are "left," "top" and "right" (defaults to "left")
44403      */
44404     /**
44405      * @cfg {Number} labelWidth
44406      * Fixed width in pixels of all field labels (defaults to undefined)
44407      */
44408     /**
44409      * @cfg {Boolean} clear
44410      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
44411      */
44412     clear : true,
44413     /**
44414      * @cfg {String} labelSeparator
44415      * The separator to use after field labels (defaults to ':')
44416      */
44417     labelSeparator : ':',
44418     /**
44419      * @cfg {Boolean} hideLabels
44420      * True to suppress the display of field labels in this layout (defaults to false)
44421      */
44422     hideLabels : false,
44423
44424     // private
44425     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
44426     
44427     isLayout : true,
44428     
44429     // private
44430     onRender : function(ct, position){
44431         if(this.el){ // from markup
44432             this.el = Roo.get(this.el);
44433         }else {  // generate
44434             var cfg = this.getAutoCreate();
44435             this.el = ct.createChild(cfg, position);
44436         }
44437         if(this.style){
44438             this.el.applyStyles(this.style);
44439         }
44440         if(this.labelAlign){
44441             this.el.addClass('x-form-label-'+this.labelAlign);
44442         }
44443         if(this.hideLabels){
44444             this.labelStyle = "display:none";
44445             this.elementStyle = "padding-left:0;";
44446         }else{
44447             if(typeof this.labelWidth == 'number'){
44448                 this.labelStyle = "width:"+this.labelWidth+"px;";
44449                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
44450             }
44451             if(this.labelAlign == 'top'){
44452                 this.labelStyle = "width:auto;";
44453                 this.elementStyle = "padding-left:0;";
44454             }
44455         }
44456         var stack = this.stack;
44457         var slen = stack.length;
44458         if(slen > 0){
44459             if(!this.fieldTpl){
44460                 var t = new Roo.Template(
44461                     '<div class="x-form-item {5}">',
44462                         '<label for="{0}" style="{2}">{1}{4}</label>',
44463                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
44464                         '</div>',
44465                     '</div><div class="x-form-clear-left"></div>'
44466                 );
44467                 t.disableFormats = true;
44468                 t.compile();
44469                 Roo.form.Layout.prototype.fieldTpl = t;
44470             }
44471             for(var i = 0; i < slen; i++) {
44472                 if(stack[i].isFormField){
44473                     this.renderField(stack[i]);
44474                 }else{
44475                     this.renderComponent(stack[i]);
44476                 }
44477             }
44478         }
44479         if(this.clear){
44480             this.el.createChild({cls:'x-form-clear'});
44481         }
44482     },
44483
44484     // private
44485     renderField : function(f){
44486         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
44487                f.id, //0
44488                f.fieldLabel, //1
44489                f.labelStyle||this.labelStyle||'', //2
44490                this.elementStyle||'', //3
44491                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
44492                f.itemCls||this.itemCls||''  //5
44493        ], true).getPrevSibling());
44494     },
44495
44496     // private
44497     renderComponent : function(c){
44498         c.render(c.isLayout ? this.el : this.el.createChild());    
44499     },
44500     /**
44501      * Adds a object form elements (using the xtype property as the factory method.)
44502      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
44503      * @param {Object} config 
44504      */
44505     addxtype : function(o)
44506     {
44507         // create the lement.
44508         o.form = this.form;
44509         var fe = Roo.factory(o, Roo.form);
44510         this.form.allItems.push(fe);
44511         this.stack.push(fe);
44512         
44513         if (fe.isFormField) {
44514             this.form.items.add(fe);
44515         }
44516          
44517         return fe;
44518     }
44519 });
44520
44521 /**
44522  * @class Roo.form.Column
44523  * @extends Roo.form.Layout
44524  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
44525  * @constructor
44526  * @param {Object} config Configuration options
44527  */
44528 Roo.form.Column = function(config){
44529     Roo.form.Column.superclass.constructor.call(this, config);
44530 };
44531
44532 Roo.extend(Roo.form.Column, Roo.form.Layout, {
44533     /**
44534      * @cfg {Number/String} width
44535      * The fixed width of the column in pixels or CSS value (defaults to "auto")
44536      */
44537     /**
44538      * @cfg {String/Object} autoCreate
44539      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
44540      */
44541
44542     // private
44543     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
44544
44545     // private
44546     onRender : function(ct, position){
44547         Roo.form.Column.superclass.onRender.call(this, ct, position);
44548         if(this.width){
44549             this.el.setWidth(this.width);
44550         }
44551     }
44552 });
44553
44554
44555 /**
44556  * @class Roo.form.Row
44557  * @extends Roo.form.Layout
44558  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
44559  * @constructor
44560  * @param {Object} config Configuration options
44561  */
44562
44563  
44564 Roo.form.Row = function(config){
44565     Roo.form.Row.superclass.constructor.call(this, config);
44566 };
44567  
44568 Roo.extend(Roo.form.Row, Roo.form.Layout, {
44569       /**
44570      * @cfg {Number/String} width
44571      * The fixed width of the column in pixels or CSS value (defaults to "auto")
44572      */
44573     /**
44574      * @cfg {Number/String} height
44575      * The fixed height of the column in pixels or CSS value (defaults to "auto")
44576      */
44577     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
44578     
44579     padWidth : 20,
44580     // private
44581     onRender : function(ct, position){
44582         //console.log('row render');
44583         if(!this.rowTpl){
44584             var t = new Roo.Template(
44585                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
44586                     '<label for="{0}" style="{2}">{1}{4}</label>',
44587                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
44588                     '</div>',
44589                 '</div>'
44590             );
44591             t.disableFormats = true;
44592             t.compile();
44593             Roo.form.Layout.prototype.rowTpl = t;
44594         }
44595         this.fieldTpl = this.rowTpl;
44596         
44597         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
44598         var labelWidth = 100;
44599         
44600         if ((this.labelAlign != 'top')) {
44601             if (typeof this.labelWidth == 'number') {
44602                 labelWidth = this.labelWidth
44603             }
44604             this.padWidth =  20 + labelWidth;
44605             
44606         }
44607         
44608         Roo.form.Column.superclass.onRender.call(this, ct, position);
44609         if(this.width){
44610             this.el.setWidth(this.width);
44611         }
44612         if(this.height){
44613             this.el.setHeight(this.height);
44614         }
44615     },
44616     
44617     // private
44618     renderField : function(f){
44619         f.fieldEl = this.fieldTpl.append(this.el, [
44620                f.id, f.fieldLabel,
44621                f.labelStyle||this.labelStyle||'',
44622                this.elementStyle||'',
44623                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
44624                f.itemCls||this.itemCls||'',
44625                f.width ? f.width + this.padWidth : 160 + this.padWidth
44626        ],true);
44627     }
44628 });
44629  
44630
44631 /**
44632  * @class Roo.form.FieldSet
44633  * @extends Roo.form.Layout
44634  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
44635  * @constructor
44636  * @param {Object} config Configuration options
44637  */
44638 Roo.form.FieldSet = function(config){
44639     Roo.form.FieldSet.superclass.constructor.call(this, config);
44640 };
44641
44642 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
44643     /**
44644      * @cfg {String} legend
44645      * The text to display as the legend for the FieldSet (defaults to '')
44646      */
44647     /**
44648      * @cfg {String/Object} autoCreate
44649      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
44650      */
44651
44652     // private
44653     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
44654
44655     // private
44656     onRender : function(ct, position){
44657         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
44658         if(this.legend){
44659             this.setLegend(this.legend);
44660         }
44661     },
44662
44663     // private
44664     setLegend : function(text){
44665         if(this.rendered){
44666             this.el.child('legend').update(text);
44667         }
44668     }
44669 });/*
44670  * Based on:
44671  * Ext JS Library 1.1.1
44672  * Copyright(c) 2006-2007, Ext JS, LLC.
44673  *
44674  * Originally Released Under LGPL - original licence link has changed is not relivant.
44675  *
44676  * Fork - LGPL
44677  * <script type="text/javascript">
44678  */
44679 /**
44680  * @class Roo.form.VTypes
44681  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
44682  * @singleton
44683  */
44684 Roo.form.VTypes = function(){
44685     // closure these in so they are only created once.
44686     var alpha = /^[a-zA-Z_]+$/;
44687     var alphanum = /^[a-zA-Z0-9_]+$/;
44688     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
44689     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
44690
44691     // All these messages and functions are configurable
44692     return {
44693         /**
44694          * The function used to validate email addresses
44695          * @param {String} value The email address
44696          */
44697         'email' : function(v){
44698             return email.test(v);
44699         },
44700         /**
44701          * The error text to display when the email validation function returns false
44702          * @type String
44703          */
44704         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
44705         /**
44706          * The keystroke filter mask to be applied on email input
44707          * @type RegExp
44708          */
44709         'emailMask' : /[a-z0-9_\.\-@]/i,
44710
44711         /**
44712          * The function used to validate URLs
44713          * @param {String} value The URL
44714          */
44715         'url' : function(v){
44716             return url.test(v);
44717         },
44718         /**
44719          * The error text to display when the url validation function returns false
44720          * @type String
44721          */
44722         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
44723         
44724         /**
44725          * The function used to validate alpha values
44726          * @param {String} value The value
44727          */
44728         'alpha' : function(v){
44729             return alpha.test(v);
44730         },
44731         /**
44732          * The error text to display when the alpha validation function returns false
44733          * @type String
44734          */
44735         'alphaText' : 'This field should only contain letters and _',
44736         /**
44737          * The keystroke filter mask to be applied on alpha input
44738          * @type RegExp
44739          */
44740         'alphaMask' : /[a-z_]/i,
44741
44742         /**
44743          * The function used to validate alphanumeric values
44744          * @param {String} value The value
44745          */
44746         'alphanum' : function(v){
44747             return alphanum.test(v);
44748         },
44749         /**
44750          * The error text to display when the alphanumeric validation function returns false
44751          * @type String
44752          */
44753         'alphanumText' : 'This field should only contain letters, numbers and _',
44754         /**
44755          * The keystroke filter mask to be applied on alphanumeric input
44756          * @type RegExp
44757          */
44758         'alphanumMask' : /[a-z0-9_]/i
44759     };
44760 }();//<script type="text/javascript">
44761
44762 /**
44763  * @class Roo.form.FCKeditor
44764  * @extends Roo.form.TextArea
44765  * Wrapper around the FCKEditor http://www.fckeditor.net
44766  * @constructor
44767  * Creates a new FCKeditor
44768  * @param {Object} config Configuration options
44769  */
44770 Roo.form.FCKeditor = function(config){
44771     Roo.form.FCKeditor.superclass.constructor.call(this, config);
44772     this.addEvents({
44773          /**
44774          * @event editorinit
44775          * Fired when the editor is initialized - you can add extra handlers here..
44776          * @param {FCKeditor} this
44777          * @param {Object} the FCK object.
44778          */
44779         editorinit : true
44780     });
44781     
44782     
44783 };
44784 Roo.form.FCKeditor.editors = { };
44785 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
44786 {
44787     //defaultAutoCreate : {
44788     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
44789     //},
44790     // private
44791     /**
44792      * @cfg {Object} fck options - see fck manual for details.
44793      */
44794     fckconfig : false,
44795     
44796     /**
44797      * @cfg {Object} fck toolbar set (Basic or Default)
44798      */
44799     toolbarSet : 'Basic',
44800     /**
44801      * @cfg {Object} fck BasePath
44802      */ 
44803     basePath : '/fckeditor/',
44804     
44805     
44806     frame : false,
44807     
44808     value : '',
44809     
44810    
44811     onRender : function(ct, position)
44812     {
44813         if(!this.el){
44814             this.defaultAutoCreate = {
44815                 tag: "textarea",
44816                 style:"width:300px;height:60px;",
44817                 autocomplete: "off"
44818             };
44819         }
44820         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
44821         /*
44822         if(this.grow){
44823             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
44824             if(this.preventScrollbars){
44825                 this.el.setStyle("overflow", "hidden");
44826             }
44827             this.el.setHeight(this.growMin);
44828         }
44829         */
44830         //console.log('onrender' + this.getId() );
44831         Roo.form.FCKeditor.editors[this.getId()] = this;
44832          
44833
44834         this.replaceTextarea() ;
44835         
44836     },
44837     
44838     getEditor : function() {
44839         return this.fckEditor;
44840     },
44841     /**
44842      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
44843      * @param {Mixed} value The value to set
44844      */
44845     
44846     
44847     setValue : function(value)
44848     {
44849         //console.log('setValue: ' + value);
44850         
44851         if(typeof(value) == 'undefined') { // not sure why this is happending...
44852             return;
44853         }
44854         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44855         
44856         //if(!this.el || !this.getEditor()) {
44857         //    this.value = value;
44858             //this.setValue.defer(100,this,[value]);    
44859         //    return;
44860         //} 
44861         
44862         if(!this.getEditor()) {
44863             return;
44864         }
44865         
44866         this.getEditor().SetData(value);
44867         
44868         //
44869
44870     },
44871
44872     /**
44873      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
44874      * @return {Mixed} value The field value
44875      */
44876     getValue : function()
44877     {
44878         
44879         if (this.frame && this.frame.dom.style.display == 'none') {
44880             return Roo.form.FCKeditor.superclass.getValue.call(this);
44881         }
44882         
44883         if(!this.el || !this.getEditor()) {
44884            
44885            // this.getValue.defer(100,this); 
44886             return this.value;
44887         }
44888        
44889         
44890         var value=this.getEditor().GetData();
44891         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44892         return Roo.form.FCKeditor.superclass.getValue.call(this);
44893         
44894
44895     },
44896
44897     /**
44898      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
44899      * @return {Mixed} value The field value
44900      */
44901     getRawValue : function()
44902     {
44903         if (this.frame && this.frame.dom.style.display == 'none') {
44904             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
44905         }
44906         
44907         if(!this.el || !this.getEditor()) {
44908             //this.getRawValue.defer(100,this); 
44909             return this.value;
44910             return;
44911         }
44912         
44913         
44914         
44915         var value=this.getEditor().GetData();
44916         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
44917         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
44918          
44919     },
44920     
44921     setSize : function(w,h) {
44922         
44923         
44924         
44925         //if (this.frame && this.frame.dom.style.display == 'none') {
44926         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
44927         //    return;
44928         //}
44929         //if(!this.el || !this.getEditor()) {
44930         //    this.setSize.defer(100,this, [w,h]); 
44931         //    return;
44932         //}
44933         
44934         
44935         
44936         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
44937         
44938         this.frame.dom.setAttribute('width', w);
44939         this.frame.dom.setAttribute('height', h);
44940         this.frame.setSize(w,h);
44941         
44942     },
44943     
44944     toggleSourceEdit : function(value) {
44945         
44946       
44947          
44948         this.el.dom.style.display = value ? '' : 'none';
44949         this.frame.dom.style.display = value ?  'none' : '';
44950         
44951     },
44952     
44953     
44954     focus: function(tag)
44955     {
44956         if (this.frame.dom.style.display == 'none') {
44957             return Roo.form.FCKeditor.superclass.focus.call(this);
44958         }
44959         if(!this.el || !this.getEditor()) {
44960             this.focus.defer(100,this, [tag]); 
44961             return;
44962         }
44963         
44964         
44965         
44966         
44967         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
44968         this.getEditor().Focus();
44969         if (tgs.length) {
44970             if (!this.getEditor().Selection.GetSelection()) {
44971                 this.focus.defer(100,this, [tag]); 
44972                 return;
44973             }
44974             
44975             
44976             var r = this.getEditor().EditorDocument.createRange();
44977             r.setStart(tgs[0],0);
44978             r.setEnd(tgs[0],0);
44979             this.getEditor().Selection.GetSelection().removeAllRanges();
44980             this.getEditor().Selection.GetSelection().addRange(r);
44981             this.getEditor().Focus();
44982         }
44983         
44984     },
44985     
44986     
44987     
44988     replaceTextarea : function()
44989     {
44990         if ( document.getElementById( this.getId() + '___Frame' ) )
44991             return ;
44992         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
44993         //{
44994             // We must check the elements firstly using the Id and then the name.
44995         var oTextarea = document.getElementById( this.getId() );
44996         
44997         var colElementsByName = document.getElementsByName( this.getId() ) ;
44998          
44999         oTextarea.style.display = 'none' ;
45000
45001         if ( oTextarea.tabIndex ) {            
45002             this.TabIndex = oTextarea.tabIndex ;
45003         }
45004         
45005         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
45006         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
45007         this.frame = Roo.get(this.getId() + '___Frame')
45008     },
45009     
45010     _getConfigHtml : function()
45011     {
45012         var sConfig = '' ;
45013
45014         for ( var o in this.fckconfig ) {
45015             sConfig += sConfig.length > 0  ? '&amp;' : '';
45016             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
45017         }
45018
45019         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
45020     },
45021     
45022     
45023     _getIFrameHtml : function()
45024     {
45025         var sFile = 'fckeditor.html' ;
45026         /* no idea what this is about..
45027         try
45028         {
45029             if ( (/fcksource=true/i).test( window.top.location.search ) )
45030                 sFile = 'fckeditor.original.html' ;
45031         }
45032         catch (e) { 
45033         */
45034
45035         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
45036         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
45037         
45038         
45039         var html = '<iframe id="' + this.getId() +
45040             '___Frame" src="' + sLink +
45041             '" width="' + this.width +
45042             '" height="' + this.height + '"' +
45043             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
45044             ' frameborder="0" scrolling="no"></iframe>' ;
45045
45046         return html ;
45047     },
45048     
45049     _insertHtmlBefore : function( html, element )
45050     {
45051         if ( element.insertAdjacentHTML )       {
45052             // IE
45053             element.insertAdjacentHTML( 'beforeBegin', html ) ;
45054         } else { // Gecko
45055             var oRange = document.createRange() ;
45056             oRange.setStartBefore( element ) ;
45057             var oFragment = oRange.createContextualFragment( html );
45058             element.parentNode.insertBefore( oFragment, element ) ;
45059         }
45060     }
45061     
45062     
45063   
45064     
45065     
45066     
45067     
45068
45069 });
45070
45071 //Roo.reg('fckeditor', Roo.form.FCKeditor);
45072
45073 function FCKeditor_OnComplete(editorInstance){
45074     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
45075     f.fckEditor = editorInstance;
45076     //console.log("loaded");
45077     f.fireEvent('editorinit', f, editorInstance);
45078
45079   
45080
45081  
45082
45083
45084
45085
45086
45087
45088
45089
45090
45091
45092
45093
45094
45095
45096
45097 //<script type="text/javascript">
45098 /**
45099  * @class Roo.form.GridField
45100  * @extends Roo.form.Field
45101  * Embed a grid (or editable grid into a form)
45102  * STATUS ALPHA
45103  * 
45104  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
45105  * it needs 
45106  * xgrid.store = Roo.data.Store
45107  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
45108  * xgrid.store.reader = Roo.data.JsonReader 
45109  * 
45110  * 
45111  * @constructor
45112  * Creates a new GridField
45113  * @param {Object} config Configuration options
45114  */
45115 Roo.form.GridField = function(config){
45116     Roo.form.GridField.superclass.constructor.call(this, config);
45117      
45118 };
45119
45120 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
45121     /**
45122      * @cfg {Number} width  - used to restrict width of grid..
45123      */
45124     width : 100,
45125     /**
45126      * @cfg {Number} height - used to restrict height of grid..
45127      */
45128     height : 50,
45129      /**
45130      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
45131          * 
45132          *}
45133      */
45134     xgrid : false, 
45135     /**
45136      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45137      * {tag: "input", type: "checkbox", autocomplete: "off"})
45138      */
45139    // defaultAutoCreate : { tag: 'div' },
45140     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45141     /**
45142      * @cfg {String} addTitle Text to include for adding a title.
45143      */
45144     addTitle : false,
45145     //
45146     onResize : function(){
45147         Roo.form.Field.superclass.onResize.apply(this, arguments);
45148     },
45149
45150     initEvents : function(){
45151         // Roo.form.Checkbox.superclass.initEvents.call(this);
45152         // has no events...
45153        
45154     },
45155
45156
45157     getResizeEl : function(){
45158         return this.wrap;
45159     },
45160
45161     getPositionEl : function(){
45162         return this.wrap;
45163     },
45164
45165     // private
45166     onRender : function(ct, position){
45167         
45168         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
45169         var style = this.style;
45170         delete this.style;
45171         
45172         Roo.form.GridField.superclass.onRender.call(this, ct, position);
45173         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
45174         this.viewEl = this.wrap.createChild({ tag: 'div' });
45175         if (style) {
45176             this.viewEl.applyStyles(style);
45177         }
45178         if (this.width) {
45179             this.viewEl.setWidth(this.width);
45180         }
45181         if (this.height) {
45182             this.viewEl.setHeight(this.height);
45183         }
45184         //if(this.inputValue !== undefined){
45185         //this.setValue(this.value);
45186         
45187         
45188         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
45189         
45190         
45191         this.grid.render();
45192         this.grid.getDataSource().on('remove', this.refreshValue, this);
45193         this.grid.getDataSource().on('update', this.refreshValue, this);
45194         this.grid.on('afteredit', this.refreshValue, this);
45195  
45196     },
45197      
45198     
45199     /**
45200      * Sets the value of the item. 
45201      * @param {String} either an object  or a string..
45202      */
45203     setValue : function(v){
45204         //this.value = v;
45205         v = v || []; // empty set..
45206         // this does not seem smart - it really only affects memoryproxy grids..
45207         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
45208             var ds = this.grid.getDataSource();
45209             // assumes a json reader..
45210             var data = {}
45211             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
45212             ds.loadData( data);
45213         }
45214         // clear selection so it does not get stale.
45215         if (this.grid.sm) { 
45216             this.grid.sm.clearSelections();
45217         }
45218         
45219         Roo.form.GridField.superclass.setValue.call(this, v);
45220         this.refreshValue();
45221         // should load data in the grid really....
45222     },
45223     
45224     // private
45225     refreshValue: function() {
45226          var val = [];
45227         this.grid.getDataSource().each(function(r) {
45228             val.push(r.data);
45229         });
45230         this.el.dom.value = Roo.encode(val);
45231     }
45232     
45233      
45234     
45235     
45236 });/*
45237  * Based on:
45238  * Ext JS Library 1.1.1
45239  * Copyright(c) 2006-2007, Ext JS, LLC.
45240  *
45241  * Originally Released Under LGPL - original licence link has changed is not relivant.
45242  *
45243  * Fork - LGPL
45244  * <script type="text/javascript">
45245  */
45246 /**
45247  * @class Roo.form.DisplayField
45248  * @extends Roo.form.Field
45249  * A generic Field to display non-editable data.
45250  * @constructor
45251  * Creates a new Display Field item.
45252  * @param {Object} config Configuration options
45253  */
45254 Roo.form.DisplayField = function(config){
45255     Roo.form.DisplayField.superclass.constructor.call(this, config);
45256     
45257 };
45258
45259 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
45260     inputType:      'hidden',
45261     allowBlank:     true,
45262     readOnly:         true,
45263     
45264  
45265     /**
45266      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45267      */
45268     focusClass : undefined,
45269     /**
45270      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45271      */
45272     fieldClass: 'x-form-field',
45273     
45274      /**
45275      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
45276      */
45277     valueRenderer: undefined,
45278     
45279     width: 100,
45280     /**
45281      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45282      * {tag: "input", type: "checkbox", autocomplete: "off"})
45283      */
45284      
45285  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45286
45287     onResize : function(){
45288         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
45289         
45290     },
45291
45292     initEvents : function(){
45293         // Roo.form.Checkbox.superclass.initEvents.call(this);
45294         // has no events...
45295        
45296     },
45297
45298
45299     getResizeEl : function(){
45300         return this.wrap;
45301     },
45302
45303     getPositionEl : function(){
45304         return this.wrap;
45305     },
45306
45307     // private
45308     onRender : function(ct, position){
45309         
45310         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
45311         //if(this.inputValue !== undefined){
45312         this.wrap = this.el.wrap();
45313         
45314         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
45315         
45316         if (this.bodyStyle) {
45317             this.viewEl.applyStyles(this.bodyStyle);
45318         }
45319         //this.viewEl.setStyle('padding', '2px');
45320         
45321         this.setValue(this.value);
45322         
45323     },
45324 /*
45325     // private
45326     initValue : Roo.emptyFn,
45327
45328   */
45329
45330         // private
45331     onClick : function(){
45332         
45333     },
45334
45335     /**
45336      * Sets the checked state of the checkbox.
45337      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
45338      */
45339     setValue : function(v){
45340         this.value = v;
45341         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
45342         // this might be called before we have a dom element..
45343         if (!this.viewEl) {
45344             return;
45345         }
45346         this.viewEl.dom.innerHTML = html;
45347         Roo.form.DisplayField.superclass.setValue.call(this, v);
45348
45349     }
45350 });/*
45351  * 
45352  * Licence- LGPL
45353  * 
45354  */
45355
45356 /**
45357  * @class Roo.form.DayPicker
45358  * @extends Roo.form.Field
45359  * A Day picker show [M] [T] [W] ....
45360  * @constructor
45361  * Creates a new Day Picker
45362  * @param {Object} config Configuration options
45363  */
45364 Roo.form.DayPicker= function(config){
45365     Roo.form.DayPicker.superclass.constructor.call(this, config);
45366      
45367 };
45368
45369 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
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 {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45381      * {tag: "input", type: "checkbox", autocomplete: "off"})
45382      */
45383     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
45384     
45385    
45386     actionMode : 'viewEl', 
45387     //
45388     // private
45389  
45390     inputType : 'hidden',
45391     
45392      
45393     inputElement: false, // real input element?
45394     basedOn: false, // ????
45395     
45396     isFormField: true, // not sure where this is needed!!!!
45397
45398     onResize : function(){
45399         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
45400         if(!this.boxLabel){
45401             this.el.alignTo(this.wrap, 'c-c');
45402         }
45403     },
45404
45405     initEvents : function(){
45406         Roo.form.Checkbox.superclass.initEvents.call(this);
45407         this.el.on("click", this.onClick,  this);
45408         this.el.on("change", this.onClick,  this);
45409     },
45410
45411
45412     getResizeEl : function(){
45413         return this.wrap;
45414     },
45415
45416     getPositionEl : function(){
45417         return this.wrap;
45418     },
45419
45420     
45421     // private
45422     onRender : function(ct, position){
45423         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45424        
45425         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
45426         
45427         var r1 = '<table><tr>';
45428         var r2 = '<tr class="x-form-daypick-icons">';
45429         for (var i=0; i < 7; i++) {
45430             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
45431             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
45432         }
45433         
45434         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
45435         viewEl.select('img').on('click', this.onClick, this);
45436         this.viewEl = viewEl;   
45437         
45438         
45439         // this will not work on Chrome!!!
45440         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
45441         this.el.on('propertychange', this.setFromHidden,  this);  //ie
45442         
45443         
45444           
45445
45446     },
45447
45448     // private
45449     initValue : Roo.emptyFn,
45450
45451     /**
45452      * Returns the checked state of the checkbox.
45453      * @return {Boolean} True if checked, else false
45454      */
45455     getValue : function(){
45456         return this.el.dom.value;
45457         
45458     },
45459
45460         // private
45461     onClick : function(e){ 
45462         //this.setChecked(!this.checked);
45463         Roo.get(e.target).toggleClass('x-menu-item-checked');
45464         this.refreshValue();
45465         //if(this.el.dom.checked != this.checked){
45466         //    this.setValue(this.el.dom.checked);
45467        // }
45468     },
45469     
45470     // private
45471     refreshValue : function()
45472     {
45473         var val = '';
45474         this.viewEl.select('img',true).each(function(e,i,n)  {
45475             val += e.is(".x-menu-item-checked") ? String(n) : '';
45476         });
45477         this.setValue(val, true);
45478     },
45479
45480     /**
45481      * Sets the checked state of the checkbox.
45482      * On is always based on a string comparison between inputValue and the param.
45483      * @param {Boolean/String} value - the value to set 
45484      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
45485      */
45486     setValue : function(v,suppressEvent){
45487         if (!this.el.dom) {
45488             return;
45489         }
45490         var old = this.el.dom.value ;
45491         this.el.dom.value = v;
45492         if (suppressEvent) {
45493             return ;
45494         }
45495          
45496         // update display..
45497         this.viewEl.select('img',true).each(function(e,i,n)  {
45498             
45499             var on = e.is(".x-menu-item-checked");
45500             var newv = v.indexOf(String(n)) > -1;
45501             if (on != newv) {
45502                 e.toggleClass('x-menu-item-checked');
45503             }
45504             
45505         });
45506         
45507         
45508         this.fireEvent('change', this, v, old);
45509         
45510         
45511     },
45512    
45513     // handle setting of hidden value by some other method!!?!?
45514     setFromHidden: function()
45515     {
45516         if(!this.el){
45517             return;
45518         }
45519         //console.log("SET FROM HIDDEN");
45520         //alert('setFrom hidden');
45521         this.setValue(this.el.dom.value);
45522     },
45523     
45524     onDestroy : function()
45525     {
45526         if(this.viewEl){
45527             Roo.get(this.viewEl).remove();
45528         }
45529          
45530         Roo.form.DayPicker.superclass.onDestroy.call(this);
45531     }
45532
45533 });/*
45534  * RooJS Library 1.1.1
45535  * Copyright(c) 2008-2011  Alan Knowles
45536  *
45537  * License - LGPL
45538  */
45539  
45540
45541 /**
45542  * @class Roo.form.ComboCheck
45543  * @extends Roo.form.ComboBox
45544  * A combobox for multiple select items.
45545  *
45546  * FIXME - could do with a reset button..
45547  * 
45548  * @constructor
45549  * Create a new ComboCheck
45550  * @param {Object} config Configuration options
45551  */
45552 Roo.form.ComboCheck = function(config){
45553     Roo.form.ComboCheck.superclass.constructor.call(this, config);
45554     // should verify some data...
45555     // like
45556     // hiddenName = required..
45557     // displayField = required
45558     // valudField == required
45559     var req= [ 'hiddenName', 'displayField', 'valueField' ];
45560     var _t = this;
45561     Roo.each(req, function(e) {
45562         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
45563             throw "Roo.form.ComboCheck : missing value for: " + e;
45564         }
45565     });
45566     
45567     
45568 };
45569
45570 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
45571      
45572      
45573     editable : false,
45574      
45575     selectedClass: 'x-menu-item-checked', 
45576     
45577     // private
45578     onRender : function(ct, position){
45579         var _t = this;
45580         
45581         
45582         
45583         if(!this.tpl){
45584             var cls = 'x-combo-list';
45585
45586             
45587             this.tpl =  new Roo.Template({
45588                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
45589                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
45590                    '<span>{' + this.displayField + '}</span>' +
45591                     '</div>' 
45592                 
45593             });
45594         }
45595  
45596         
45597         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
45598         this.view.singleSelect = false;
45599         this.view.multiSelect = true;
45600         this.view.toggleSelect = true;
45601         this.pageTb.add(new Roo.Toolbar.Fill(), {
45602             
45603             text: 'Done',
45604             handler: function()
45605             {
45606                 _t.collapse();
45607             }
45608         });
45609     },
45610     
45611     onViewOver : function(e, t){
45612         // do nothing...
45613         return;
45614         
45615     },
45616     
45617     onViewClick : function(doFocus,index){
45618         return;
45619         
45620     },
45621     select: function () {
45622         //Roo.log("SELECT CALLED");
45623     },
45624      
45625     selectByValue : function(xv, scrollIntoView){
45626         var ar = this.getValueArray();
45627         var sels = [];
45628         
45629         Roo.each(ar, function(v) {
45630             if(v === undefined || v === null){
45631                 return;
45632             }
45633             var r = this.findRecord(this.valueField, v);
45634             if(r){
45635                 sels.push(this.store.indexOf(r))
45636                 
45637             }
45638         },this);
45639         this.view.select(sels);
45640         return false;
45641     },
45642     
45643     
45644     
45645     onSelect : function(record, index){
45646        // Roo.log("onselect Called");
45647        // this is only called by the clear button now..
45648         this.view.clearSelections();
45649         this.setValue('[]');
45650         if (this.value != this.valueBefore) {
45651             this.fireEvent('change', this, this.value, this.valueBefore);
45652             this.valueBefore = this.value;
45653         }
45654     },
45655     getValueArray : function()
45656     {
45657         var ar = [] ;
45658         
45659         try {
45660             //Roo.log(this.value);
45661             if (typeof(this.value) == 'undefined') {
45662                 return [];
45663             }
45664             var ar = Roo.decode(this.value);
45665             return  ar instanceof Array ? ar : []; //?? valid?
45666             
45667         } catch(e) {
45668             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
45669             return [];
45670         }
45671          
45672     },
45673     expand : function ()
45674     {
45675         
45676         Roo.form.ComboCheck.superclass.expand.call(this);
45677         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
45678         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
45679         
45680
45681     },
45682     
45683     collapse : function(){
45684         Roo.form.ComboCheck.superclass.collapse.call(this);
45685         var sl = this.view.getSelectedIndexes();
45686         var st = this.store;
45687         var nv = [];
45688         var tv = [];
45689         var r;
45690         Roo.each(sl, function(i) {
45691             r = st.getAt(i);
45692             nv.push(r.get(this.valueField));
45693         },this);
45694         this.setValue(Roo.encode(nv));
45695         if (this.value != this.valueBefore) {
45696
45697             this.fireEvent('change', this, this.value, this.valueBefore);
45698             this.valueBefore = this.value;
45699         }
45700         
45701     },
45702     
45703     setValue : function(v){
45704         // Roo.log(v);
45705         this.value = v;
45706         
45707         var vals = this.getValueArray();
45708         var tv = [];
45709         Roo.each(vals, function(k) {
45710             var r = this.findRecord(this.valueField, k);
45711             if(r){
45712                 tv.push(r.data[this.displayField]);
45713             }else if(this.valueNotFoundText !== undefined){
45714                 tv.push( this.valueNotFoundText );
45715             }
45716         },this);
45717        // Roo.log(tv);
45718         
45719         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
45720         this.hiddenField.value = v;
45721         this.value = v;
45722     }
45723     
45724 });/*
45725  * Based on:
45726  * Ext JS Library 1.1.1
45727  * Copyright(c) 2006-2007, Ext JS, LLC.
45728  *
45729  * Originally Released Under LGPL - original licence link has changed is not relivant.
45730  *
45731  * Fork - LGPL
45732  * <script type="text/javascript">
45733  */
45734  
45735 /**
45736  * @class Roo.form.Signature
45737  * @extends Roo.form.Field
45738  * Signature field.  
45739  * @constructor
45740  * 
45741  * @param {Object} config Configuration options
45742  */
45743
45744 Roo.form.Signature = function(config){
45745     Roo.form.Signature.superclass.constructor.call(this, config);
45746     
45747     this.addEvents({// not in used??
45748          /**
45749          * @event confirm
45750          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
45751              * @param {Roo.form.Signature} combo This combo box
45752              */
45753         'confirm' : true,
45754         /**
45755          * @event reset
45756          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
45757              * @param {Roo.form.ComboBox} combo This combo box
45758              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
45759              */
45760         'reset' : true
45761     });
45762 };
45763
45764 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
45765     /**
45766      * @cfg {Object} labels Label to use when rendering a form.
45767      * defaults to 
45768      * labels : { 
45769      *      clear : "Clear",
45770      *      confirm : "Confirm"
45771      *  }
45772      */
45773     labels : { 
45774         clear : "Clear",
45775         confirm : "Confirm"
45776     },
45777     /**
45778      * @cfg {Number} width The signature panel width (defaults to 300)
45779      */
45780     width: 300,
45781     /**
45782      * @cfg {Number} height The signature panel height (defaults to 100)
45783      */
45784     height : 100,
45785     /**
45786      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
45787      */
45788     allowBlank : false,
45789     
45790     //private
45791     // {Object} signPanel The signature SVG panel element (defaults to {})
45792     signPanel : {},
45793     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
45794     isMouseDown : false,
45795     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
45796     isConfirmed : false,
45797     // {String} signatureTmp SVG mapping string (defaults to empty string)
45798     signatureTmp : '',
45799     
45800     
45801     defaultAutoCreate : { // modified by initCompnoent..
45802         tag: "input",
45803         type:"hidden"
45804     },
45805
45806     // private
45807     onRender : function(ct, position){
45808         
45809         Roo.form.Signature.superclass.onRender.call(this, ct, position);
45810         
45811         this.wrap = this.el.wrap({
45812             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
45813         });
45814         
45815         this.createToolbar(this);
45816         this.signPanel = this.wrap.createChild({
45817                 tag: 'div',
45818                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
45819             }, this.el
45820         );
45821             
45822         this.svgID = Roo.id();
45823         this.svgEl = this.signPanel.createChild({
45824               xmlns : 'http://www.w3.org/2000/svg',
45825               tag : 'svg',
45826               id : this.svgID + "-svg",
45827               width: this.width,
45828               height: this.height,
45829               viewBox: '0 0 '+this.width+' '+this.height,
45830               cn : [
45831                 {
45832                     tag: "rect",
45833                     id: this.svgID + "-svg-r",
45834                     width: this.width,
45835                     height: this.height,
45836                     fill: "#ffa"
45837                 },
45838                 {
45839                     tag: "line",
45840                     id: this.svgID + "-svg-l",
45841                     x1: "0", // start
45842                     y1: (this.height*0.8), // start set the line in 80% of height
45843                     x2: this.width, // end
45844                     y2: (this.height*0.8), // end set the line in 80% of height
45845                     'stroke': "#666",
45846                     'stroke-width': "1",
45847                     'stroke-dasharray': "3",
45848                     'shape-rendering': "crispEdges",
45849                     'pointer-events': "none"
45850                 },
45851                 {
45852                     tag: "path",
45853                     id: this.svgID + "-svg-p",
45854                     'stroke': "navy",
45855                     'stroke-width': "3",
45856                     'fill': "none",
45857                     'pointer-events': 'none'
45858                 }
45859               ]
45860         });
45861         this.createSVG();
45862         this.svgBox = this.svgEl.dom.getScreenCTM();
45863     },
45864     createSVG : function(){ 
45865         var svg = this.signPanel;
45866         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
45867         var t = this;
45868
45869         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
45870         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
45871         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
45872         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
45873         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
45874         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
45875         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
45876         
45877     },
45878     isTouchEvent : function(e){
45879         return e.type.match(/^touch/);
45880     },
45881     getCoords : function (e) {
45882         var pt    = this.svgEl.dom.createSVGPoint();
45883         pt.x = e.clientX; 
45884         pt.y = e.clientY;
45885         if (this.isTouchEvent(e)) {
45886             pt.x =  e.targetTouches[0].clientX 
45887             pt.y = e.targetTouches[0].clientY;
45888         }
45889         var a = this.svgEl.dom.getScreenCTM();
45890         var b = a.inverse();
45891         var mx = pt.matrixTransform(b);
45892         return mx.x + ',' + mx.y;
45893     },
45894     //mouse event headler 
45895     down : function (e) {
45896         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
45897         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
45898         
45899         this.isMouseDown = true;
45900         
45901         e.preventDefault();
45902     },
45903     move : function (e) {
45904         if (this.isMouseDown) {
45905             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
45906             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
45907         }
45908         
45909         e.preventDefault();
45910     },
45911     up : function (e) {
45912         this.isMouseDown = false;
45913         var sp = this.signatureTmp.split(' ');
45914         
45915         if(sp.length > 1){
45916             if(!sp[sp.length-2].match(/^L/)){
45917                 sp.pop();
45918                 sp.pop();
45919                 sp.push("");
45920                 this.signatureTmp = sp.join(" ");
45921             }
45922         }
45923         if(this.getValue() != this.signatureTmp){
45924             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
45925             this.isConfirmed = false;
45926         }
45927         e.preventDefault();
45928     },
45929     
45930     /**
45931      * Protected method that will not generally be called directly. It
45932      * is called when the editor creates its toolbar. Override this method if you need to
45933      * add custom toolbar buttons.
45934      * @param {HtmlEditor} editor
45935      */
45936     createToolbar : function(editor){
45937          function btn(id, toggle, handler){
45938             var xid = fid + '-'+ id ;
45939             return {
45940                 id : xid,
45941                 cmd : id,
45942                 cls : 'x-btn-icon x-edit-'+id,
45943                 enableToggle:toggle !== false,
45944                 scope: editor, // was editor...
45945                 handler:handler||editor.relayBtnCmd,
45946                 clickEvent:'mousedown',
45947                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45948                 tabIndex:-1
45949             };
45950         }
45951         
45952         
45953         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45954         this.tb = tb;
45955         this.tb.add(
45956            {
45957                 cls : ' x-signature-btn x-signature-'+id,
45958                 scope: editor, // was editor...
45959                 handler: this.reset,
45960                 clickEvent:'mousedown',
45961                 text: this.labels.clear
45962             },
45963             {
45964                  xtype : 'Fill',
45965                  xns: Roo.Toolbar
45966             }, 
45967             {
45968                 cls : '  x-signature-btn x-signature-'+id,
45969                 scope: editor, // was editor...
45970                 handler: this.confirmHandler,
45971                 clickEvent:'mousedown',
45972                 text: this.labels.confirm
45973             }
45974         );
45975     
45976     },
45977     //public
45978     /**
45979      * when user is clicked confirm then show this image.....
45980      * 
45981      * @return {String} Image Data URI
45982      */
45983     getImageDataURI : function(){
45984         var svg = this.svgEl.dom.parentNode.innerHTML;
45985         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
45986         return src; 
45987     },
45988     /**
45989      * 
45990      * @return {Boolean} this.isConfirmed
45991      */
45992     getConfirmed : function(){
45993         return this.isConfirmed;
45994     },
45995     /**
45996      * 
45997      * @return {Number} this.width
45998      */
45999     getWidth : function(){
46000         return this.width;
46001     },
46002     /**
46003      * 
46004      * @return {Number} this.height
46005      */
46006     getHeight : function(){
46007         return this.height;
46008     },
46009     // private
46010     getSignature : function(){
46011         return this.signatureTmp;
46012     },
46013     // private
46014     reset : function(){
46015         this.signatureTmp = '';
46016         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46017         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
46018         this.isConfirmed = false;
46019         Roo.form.Signature.superclass.reset.call(this);
46020     },
46021     setSignature : function(s){
46022         this.signatureTmp = s;
46023         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46024         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
46025         this.setValue(s);
46026         this.isConfirmed = false;
46027         Roo.form.Signature.superclass.reset.call(this);
46028     }, 
46029     test : function(){
46030 //        Roo.log(this.signPanel.dom.contentWindow.up())
46031     },
46032     //private
46033     setConfirmed : function(){
46034         
46035         
46036         
46037 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
46038     },
46039     // private
46040     confirmHandler : function(){
46041         if(!this.getSignature()){
46042             return;
46043         }
46044         
46045         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
46046         this.setValue(this.getSignature());
46047         this.isConfirmed = true;
46048         
46049         this.fireEvent('confirm', this);
46050     },
46051     // private
46052     // Subclasses should provide the validation implementation by overriding this
46053     validateValue : function(value){
46054         if(this.allowBlank){
46055             return true;
46056         }
46057         
46058         if(this.isConfirmed){
46059             return true;
46060         }
46061         return false;
46062     }
46063 });/*
46064  * Based on:
46065  * Ext JS Library 1.1.1
46066  * Copyright(c) 2006-2007, Ext JS, LLC.
46067  *
46068  * Originally Released Under LGPL - original licence link has changed is not relivant.
46069  *
46070  * Fork - LGPL
46071  * <script type="text/javascript">
46072  */
46073  
46074
46075 /**
46076  * @class Roo.form.ComboBox
46077  * @extends Roo.form.TriggerField
46078  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
46079  * @constructor
46080  * Create a new ComboBox.
46081  * @param {Object} config Configuration options
46082  */
46083 Roo.form.Select = function(config){
46084     Roo.form.Select.superclass.constructor.call(this, config);
46085      
46086 };
46087
46088 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
46089     /**
46090      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
46091      */
46092     /**
46093      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
46094      * rendering into an Roo.Editor, defaults to false)
46095      */
46096     /**
46097      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
46098      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
46099      */
46100     /**
46101      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
46102      */
46103     /**
46104      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
46105      * the dropdown list (defaults to undefined, with no header element)
46106      */
46107
46108      /**
46109      * @cfg {String/Roo.Template} tpl The template to use to render the output
46110      */
46111      
46112     // private
46113     defaultAutoCreate : {tag: "select"  },
46114     /**
46115      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
46116      */
46117     listWidth: undefined,
46118     /**
46119      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
46120      * mode = 'remote' or 'text' if mode = 'local')
46121      */
46122     displayField: undefined,
46123     /**
46124      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
46125      * mode = 'remote' or 'value' if mode = 'local'). 
46126      * Note: use of a valueField requires the user make a selection
46127      * in order for a value to be mapped.
46128      */
46129     valueField: undefined,
46130     
46131     
46132     /**
46133      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
46134      * field's data value (defaults to the underlying DOM element's name)
46135      */
46136     hiddenName: undefined,
46137     /**
46138      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
46139      */
46140     listClass: '',
46141     /**
46142      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
46143      */
46144     selectedClass: 'x-combo-selected',
46145     /**
46146      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
46147      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
46148      * which displays a downward arrow icon).
46149      */
46150     triggerClass : 'x-form-arrow-trigger',
46151     /**
46152      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
46153      */
46154     shadow:'sides',
46155     /**
46156      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
46157      * anchor positions (defaults to 'tl-bl')
46158      */
46159     listAlign: 'tl-bl?',
46160     /**
46161      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
46162      */
46163     maxHeight: 300,
46164     /**
46165      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
46166      * query specified by the allQuery config option (defaults to 'query')
46167      */
46168     triggerAction: 'query',
46169     /**
46170      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
46171      * (defaults to 4, does not apply if editable = false)
46172      */
46173     minChars : 4,
46174     /**
46175      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
46176      * delay (typeAheadDelay) if it matches a known value (defaults to false)
46177      */
46178     typeAhead: false,
46179     /**
46180      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
46181      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
46182      */
46183     queryDelay: 500,
46184     /**
46185      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
46186      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
46187      */
46188     pageSize: 0,
46189     /**
46190      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
46191      * when editable = true (defaults to false)
46192      */
46193     selectOnFocus:false,
46194     /**
46195      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
46196      */
46197     queryParam: 'query',
46198     /**
46199      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
46200      * when mode = 'remote' (defaults to 'Loading...')
46201      */
46202     loadingText: 'Loading...',
46203     /**
46204      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
46205      */
46206     resizable: false,
46207     /**
46208      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
46209      */
46210     handleHeight : 8,
46211     /**
46212      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
46213      * traditional select (defaults to true)
46214      */
46215     editable: true,
46216     /**
46217      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
46218      */
46219     allQuery: '',
46220     /**
46221      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
46222      */
46223     mode: 'remote',
46224     /**
46225      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
46226      * listWidth has a higher value)
46227      */
46228     minListWidth : 70,
46229     /**
46230      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
46231      * allow the user to set arbitrary text into the field (defaults to false)
46232      */
46233     forceSelection:false,
46234     /**
46235      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
46236      * if typeAhead = true (defaults to 250)
46237      */
46238     typeAheadDelay : 250,
46239     /**
46240      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
46241      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
46242      */
46243     valueNotFoundText : undefined,
46244     
46245     /**
46246      * @cfg {String} defaultValue The value displayed after loading the store.
46247      */
46248     defaultValue: '',
46249     
46250     /**
46251      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
46252      */
46253     blockFocus : false,
46254     
46255     /**
46256      * @cfg {Boolean} disableClear Disable showing of clear button.
46257      */
46258     disableClear : false,
46259     /**
46260      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
46261      */
46262     alwaysQuery : false,
46263     
46264     //private
46265     addicon : false,
46266     editicon: false,
46267     
46268     // element that contains real text value.. (when hidden is used..)
46269      
46270     // private
46271     onRender : function(ct, position){
46272         Roo.form.Field.prototype.onRender.call(this, ct, position);
46273         
46274         if(this.store){
46275             this.store.on('beforeload', this.onBeforeLoad, this);
46276             this.store.on('load', this.onLoad, this);
46277             this.store.on('loadexception', this.onLoadException, this);
46278             this.store.load({});
46279         }
46280         
46281         
46282         
46283     },
46284
46285     // private
46286     initEvents : function(){
46287         //Roo.form.ComboBox.superclass.initEvents.call(this);
46288  
46289     },
46290
46291     onDestroy : function(){
46292        
46293         if(this.store){
46294             this.store.un('beforeload', this.onBeforeLoad, this);
46295             this.store.un('load', this.onLoad, this);
46296             this.store.un('loadexception', this.onLoadException, this);
46297         }
46298         //Roo.form.ComboBox.superclass.onDestroy.call(this);
46299     },
46300
46301     // private
46302     fireKey : function(e){
46303         if(e.isNavKeyPress() && !this.list.isVisible()){
46304             this.fireEvent("specialkey", this, e);
46305         }
46306     },
46307
46308     // private
46309     onResize: function(w, h){
46310         
46311         return; 
46312     
46313         
46314     },
46315
46316     /**
46317      * Allow or prevent the user from directly editing the field text.  If false is passed,
46318      * the user will only be able to select from the items defined in the dropdown list.  This method
46319      * is the runtime equivalent of setting the 'editable' config option at config time.
46320      * @param {Boolean} value True to allow the user to directly edit the field text
46321      */
46322     setEditable : function(value){
46323          
46324     },
46325
46326     // private
46327     onBeforeLoad : function(){
46328         
46329         Roo.log("Select before load");
46330         return;
46331     
46332         this.innerList.update(this.loadingText ?
46333                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
46334         //this.restrictHeight();
46335         this.selectedIndex = -1;
46336     },
46337
46338     // private
46339     onLoad : function(){
46340
46341     
46342         var dom = this.el.dom;
46343         dom.innerHTML = '';
46344          var od = dom.ownerDocument;
46345          
46346         if (this.emptyText) {
46347             var op = od.createElement('option');
46348             op.setAttribute('value', '');
46349             op.innerHTML = String.format('{0}', this.emptyText);
46350             dom.appendChild(op);
46351         }
46352         if(this.store.getCount() > 0){
46353            
46354             var vf = this.valueField;
46355             var df = this.displayField;
46356             this.store.data.each(function(r) {
46357                 // which colmsn to use... testing - cdoe / title..
46358                 var op = od.createElement('option');
46359                 op.setAttribute('value', r.data[vf]);
46360                 op.innerHTML = String.format('{0}', r.data[df]);
46361                 dom.appendChild(op);
46362             });
46363             if (typeof(this.defaultValue != 'undefined')) {
46364                 this.setValue(this.defaultValue);
46365             }
46366             
46367              
46368         }else{
46369             //this.onEmptyResults();
46370         }
46371         //this.el.focus();
46372     },
46373     // private
46374     onLoadException : function()
46375     {
46376         dom.innerHTML = '';
46377             
46378         Roo.log("Select on load exception");
46379         return;
46380     
46381         this.collapse();
46382         Roo.log(this.store.reader.jsonData);
46383         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
46384             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
46385         }
46386         
46387         
46388     },
46389     // private
46390     onTypeAhead : function(){
46391          
46392     },
46393
46394     // private
46395     onSelect : function(record, index){
46396         Roo.log('on select?');
46397         return;
46398         if(this.fireEvent('beforeselect', this, record, index) !== false){
46399             this.setFromData(index > -1 ? record.data : false);
46400             this.collapse();
46401             this.fireEvent('select', this, record, index);
46402         }
46403     },
46404
46405     /**
46406      * Returns the currently selected field value or empty string if no value is set.
46407      * @return {String} value The selected value
46408      */
46409     getValue : function(){
46410         var dom = this.el.dom;
46411         this.value = dom.options[dom.selectedIndex].value;
46412         return this.value;
46413         
46414     },
46415
46416     /**
46417      * Clears any text/value currently set in the field
46418      */
46419     clearValue : function(){
46420         this.value = '';
46421         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
46422         
46423     },
46424
46425     /**
46426      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
46427      * will be displayed in the field.  If the value does not match the data value of an existing item,
46428      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
46429      * Otherwise the field will be blank (although the value will still be set).
46430      * @param {String} value The value to match
46431      */
46432     setValue : function(v){
46433         var d = this.el.dom;
46434         for (var i =0; i < d.options.length;i++) {
46435             if (v == d.options[i].value) {
46436                 d.selectedIndex = i;
46437                 this.value = v;
46438                 return;
46439             }
46440         }
46441         this.clearValue();
46442     },
46443     /**
46444      * @property {Object} the last set data for the element
46445      */
46446     
46447     lastData : false,
46448     /**
46449      * Sets the value of the field based on a object which is related to the record format for the store.
46450      * @param {Object} value the value to set as. or false on reset?
46451      */
46452     setFromData : function(o){
46453         Roo.log('setfrom data?');
46454          
46455         
46456         
46457     },
46458     // private
46459     reset : function(){
46460         this.clearValue();
46461     },
46462     // private
46463     findRecord : function(prop, value){
46464         
46465         return false;
46466     
46467         var record;
46468         if(this.store.getCount() > 0){
46469             this.store.each(function(r){
46470                 if(r.data[prop] == value){
46471                     record = r;
46472                     return false;
46473                 }
46474                 return true;
46475             });
46476         }
46477         return record;
46478     },
46479     
46480     getName: function()
46481     {
46482         // returns hidden if it's set..
46483         if (!this.rendered) {return ''};
46484         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
46485         
46486     },
46487      
46488
46489     
46490
46491     // private
46492     onEmptyResults : function(){
46493         Roo.log('empty results');
46494         //this.collapse();
46495     },
46496
46497     /**
46498      * Returns true if the dropdown list is expanded, else false.
46499      */
46500     isExpanded : function(){
46501         return false;
46502     },
46503
46504     /**
46505      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
46506      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
46507      * @param {String} value The data value of the item to select
46508      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
46509      * selected item if it is not currently in view (defaults to true)
46510      * @return {Boolean} True if the value matched an item in the list, else false
46511      */
46512     selectByValue : function(v, scrollIntoView){
46513         Roo.log('select By Value');
46514         return false;
46515     
46516         if(v !== undefined && v !== null){
46517             var r = this.findRecord(this.valueField || this.displayField, v);
46518             if(r){
46519                 this.select(this.store.indexOf(r), scrollIntoView);
46520                 return true;
46521             }
46522         }
46523         return false;
46524     },
46525
46526     /**
46527      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
46528      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
46529      * @param {Number} index The zero-based index of the list item to select
46530      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
46531      * selected item if it is not currently in view (defaults to true)
46532      */
46533     select : function(index, scrollIntoView){
46534         Roo.log('select ');
46535         return  ;
46536         
46537         this.selectedIndex = index;
46538         this.view.select(index);
46539         if(scrollIntoView !== false){
46540             var el = this.view.getNode(index);
46541             if(el){
46542                 this.innerList.scrollChildIntoView(el, false);
46543             }
46544         }
46545     },
46546
46547       
46548
46549     // private
46550     validateBlur : function(){
46551         
46552         return;
46553         
46554     },
46555
46556     // private
46557     initQuery : function(){
46558         this.doQuery(this.getRawValue());
46559     },
46560
46561     // private
46562     doForce : function(){
46563         if(this.el.dom.value.length > 0){
46564             this.el.dom.value =
46565                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
46566              
46567         }
46568     },
46569
46570     /**
46571      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
46572      * query allowing the query action to be canceled if needed.
46573      * @param {String} query The SQL query to execute
46574      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
46575      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
46576      * saved in the current store (defaults to false)
46577      */
46578     doQuery : function(q, forceAll){
46579         
46580         Roo.log('doQuery?');
46581         if(q === undefined || q === null){
46582             q = '';
46583         }
46584         var qe = {
46585             query: q,
46586             forceAll: forceAll,
46587             combo: this,
46588             cancel:false
46589         };
46590         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
46591             return false;
46592         }
46593         q = qe.query;
46594         forceAll = qe.forceAll;
46595         if(forceAll === true || (q.length >= this.minChars)){
46596             if(this.lastQuery != q || this.alwaysQuery){
46597                 this.lastQuery = q;
46598                 if(this.mode == 'local'){
46599                     this.selectedIndex = -1;
46600                     if(forceAll){
46601                         this.store.clearFilter();
46602                     }else{
46603                         this.store.filter(this.displayField, q);
46604                     }
46605                     this.onLoad();
46606                 }else{
46607                     this.store.baseParams[this.queryParam] = q;
46608                     this.store.load({
46609                         params: this.getParams(q)
46610                     });
46611                     this.expand();
46612                 }
46613             }else{
46614                 this.selectedIndex = -1;
46615                 this.onLoad();   
46616             }
46617         }
46618     },
46619
46620     // private
46621     getParams : function(q){
46622         var p = {};
46623         //p[this.queryParam] = q;
46624         if(this.pageSize){
46625             p.start = 0;
46626             p.limit = this.pageSize;
46627         }
46628         return p;
46629     },
46630
46631     /**
46632      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
46633      */
46634     collapse : function(){
46635         
46636     },
46637
46638     // private
46639     collapseIf : function(e){
46640         
46641     },
46642
46643     /**
46644      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
46645      */
46646     expand : function(){
46647         
46648     } ,
46649
46650     // private
46651      
46652
46653     /** 
46654     * @cfg {Boolean} grow 
46655     * @hide 
46656     */
46657     /** 
46658     * @cfg {Number} growMin 
46659     * @hide 
46660     */
46661     /** 
46662     * @cfg {Number} growMax 
46663     * @hide 
46664     */
46665     /**
46666      * @hide
46667      * @method autoSize
46668      */
46669     
46670     setWidth : function()
46671     {
46672         
46673     },
46674     getResizeEl : function(){
46675         return this.el;
46676     }
46677 });//<script type="text/javasscript">
46678  
46679
46680 /**
46681  * @class Roo.DDView
46682  * A DnD enabled version of Roo.View.
46683  * @param {Element/String} container The Element in which to create the View.
46684  * @param {String} tpl The template string used to create the markup for each element of the View
46685  * @param {Object} config The configuration properties. These include all the config options of
46686  * {@link Roo.View} plus some specific to this class.<br>
46687  * <p>
46688  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
46689  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
46690  * <p>
46691  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
46692 .x-view-drag-insert-above {
46693         border-top:1px dotted #3366cc;
46694 }
46695 .x-view-drag-insert-below {
46696         border-bottom:1px dotted #3366cc;
46697 }
46698 </code></pre>
46699  * 
46700  */
46701  
46702 Roo.DDView = function(container, tpl, config) {
46703     Roo.DDView.superclass.constructor.apply(this, arguments);
46704     this.getEl().setStyle("outline", "0px none");
46705     this.getEl().unselectable();
46706     if (this.dragGroup) {
46707                 this.setDraggable(this.dragGroup.split(","));
46708     }
46709     if (this.dropGroup) {
46710                 this.setDroppable(this.dropGroup.split(","));
46711     }
46712     if (this.deletable) {
46713         this.setDeletable();
46714     }
46715     this.isDirtyFlag = false;
46716         this.addEvents({
46717                 "drop" : true
46718         });
46719 };
46720
46721 Roo.extend(Roo.DDView, Roo.View, {
46722 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
46723 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
46724 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
46725 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
46726
46727         isFormField: true,
46728
46729         reset: Roo.emptyFn,
46730         
46731         clearInvalid: Roo.form.Field.prototype.clearInvalid,
46732
46733         validate: function() {
46734                 return true;
46735         },
46736         
46737         destroy: function() {
46738                 this.purgeListeners();
46739                 this.getEl.removeAllListeners();
46740                 this.getEl().remove();
46741                 if (this.dragZone) {
46742                         if (this.dragZone.destroy) {
46743                                 this.dragZone.destroy();
46744                         }
46745                 }
46746                 if (this.dropZone) {
46747                         if (this.dropZone.destroy) {
46748                                 this.dropZone.destroy();
46749                         }
46750                 }
46751         },
46752
46753 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
46754         getName: function() {
46755                 return this.name;
46756         },
46757
46758 /**     Loads the View from a JSON string representing the Records to put into the Store. */
46759         setValue: function(v) {
46760                 if (!this.store) {
46761                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
46762                 }
46763                 var data = {};
46764                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
46765                 this.store.proxy = new Roo.data.MemoryProxy(data);
46766                 this.store.load();
46767         },
46768
46769 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
46770         getValue: function() {
46771                 var result = '(';
46772                 this.store.each(function(rec) {
46773                         result += rec.id + ',';
46774                 });
46775                 return result.substr(0, result.length - 1) + ')';
46776         },
46777         
46778         getIds: function() {
46779                 var i = 0, result = new Array(this.store.getCount());
46780                 this.store.each(function(rec) {
46781                         result[i++] = rec.id;
46782                 });
46783                 return result;
46784         },
46785         
46786         isDirty: function() {
46787                 return this.isDirtyFlag;
46788         },
46789
46790 /**
46791  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
46792  *      whole Element becomes the target, and this causes the drop gesture to append.
46793  */
46794     getTargetFromEvent : function(e) {
46795                 var target = e.getTarget();
46796                 while ((target !== null) && (target.parentNode != this.el.dom)) {
46797                 target = target.parentNode;
46798                 }
46799                 if (!target) {
46800                         target = this.el.dom.lastChild || this.el.dom;
46801                 }
46802                 return target;
46803     },
46804
46805 /**
46806  *      Create the drag data which consists of an object which has the property "ddel" as
46807  *      the drag proxy element. 
46808  */
46809     getDragData : function(e) {
46810         var target = this.findItemFromChild(e.getTarget());
46811                 if(target) {
46812                         this.handleSelection(e);
46813                         var selNodes = this.getSelectedNodes();
46814             var dragData = {
46815                 source: this,
46816                 copy: this.copy || (this.allowCopy && e.ctrlKey),
46817                 nodes: selNodes,
46818                 records: []
46819                         };
46820                         var selectedIndices = this.getSelectedIndexes();
46821                         for (var i = 0; i < selectedIndices.length; i++) {
46822                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
46823                         }
46824                         if (selNodes.length == 1) {
46825                                 dragData.ddel = target.cloneNode(true); // the div element
46826                         } else {
46827                                 var div = document.createElement('div'); // create the multi element drag "ghost"
46828                                 div.className = 'multi-proxy';
46829                                 for (var i = 0, len = selNodes.length; i < len; i++) {
46830                                         div.appendChild(selNodes[i].cloneNode(true));
46831                                 }
46832                                 dragData.ddel = div;
46833                         }
46834             //console.log(dragData)
46835             //console.log(dragData.ddel.innerHTML)
46836                         return dragData;
46837                 }
46838         //console.log('nodragData')
46839                 return false;
46840     },
46841     
46842 /**     Specify to which ddGroup items in this DDView may be dragged. */
46843     setDraggable: function(ddGroup) {
46844         if (ddGroup instanceof Array) {
46845                 Roo.each(ddGroup, this.setDraggable, this);
46846                 return;
46847         }
46848         if (this.dragZone) {
46849                 this.dragZone.addToGroup(ddGroup);
46850         } else {
46851                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
46852                                 containerScroll: true,
46853                                 ddGroup: ddGroup 
46854
46855                         });
46856 //                      Draggability implies selection. DragZone's mousedown selects the element.
46857                         if (!this.multiSelect) { this.singleSelect = true; }
46858
46859 //                      Wire the DragZone's handlers up to methods in *this*
46860                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
46861                 }
46862     },
46863
46864 /**     Specify from which ddGroup this DDView accepts drops. */
46865     setDroppable: function(ddGroup) {
46866         if (ddGroup instanceof Array) {
46867                 Roo.each(ddGroup, this.setDroppable, this);
46868                 return;
46869         }
46870         if (this.dropZone) {
46871                 this.dropZone.addToGroup(ddGroup);
46872         } else {
46873                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
46874                                 containerScroll: true,
46875                                 ddGroup: ddGroup
46876                         });
46877
46878 //                      Wire the DropZone's handlers up to methods in *this*
46879                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
46880                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
46881                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
46882                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
46883                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
46884                 }
46885     },
46886
46887 /**     Decide whether to drop above or below a View node. */
46888     getDropPoint : function(e, n, dd){
46889         if (n == this.el.dom) { return "above"; }
46890                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
46891                 var c = t + (b - t) / 2;
46892                 var y = Roo.lib.Event.getPageY(e);
46893                 if(y <= c) {
46894                         return "above";
46895                 }else{
46896                         return "below";
46897                 }
46898     },
46899
46900     onNodeEnter : function(n, dd, e, data){
46901                 return false;
46902     },
46903     
46904     onNodeOver : function(n, dd, e, data){
46905                 var pt = this.getDropPoint(e, n, dd);
46906                 // set the insert point style on the target node
46907                 var dragElClass = this.dropNotAllowed;
46908                 if (pt) {
46909                         var targetElClass;
46910                         if (pt == "above"){
46911                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
46912                                 targetElClass = "x-view-drag-insert-above";
46913                         } else {
46914                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
46915                                 targetElClass = "x-view-drag-insert-below";
46916                         }
46917                         if (this.lastInsertClass != targetElClass){
46918                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
46919                                 this.lastInsertClass = targetElClass;
46920                         }
46921                 }
46922                 return dragElClass;
46923         },
46924
46925     onNodeOut : function(n, dd, e, data){
46926                 this.removeDropIndicators(n);
46927     },
46928
46929     onNodeDrop : function(n, dd, e, data){
46930         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
46931                 return false;
46932         }
46933         var pt = this.getDropPoint(e, n, dd);
46934                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
46935                 if (pt == "below") { insertAt++; }
46936                 for (var i = 0; i < data.records.length; i++) {
46937                         var r = data.records[i];
46938                         var dup = this.store.getById(r.id);
46939                         if (dup && (dd != this.dragZone)) {
46940                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
46941                         } else {
46942                                 if (data.copy) {
46943                                         this.store.insert(insertAt++, r.copy());
46944                                 } else {
46945                                         data.source.isDirtyFlag = true;
46946                                         r.store.remove(r);
46947                                         this.store.insert(insertAt++, r);
46948                                 }
46949                                 this.isDirtyFlag = true;
46950                         }
46951                 }
46952                 this.dragZone.cachedTarget = null;
46953                 return true;
46954     },
46955
46956     removeDropIndicators : function(n){
46957                 if(n){
46958                         Roo.fly(n).removeClass([
46959                                 "x-view-drag-insert-above",
46960                                 "x-view-drag-insert-below"]);
46961                         this.lastInsertClass = "_noclass";
46962                 }
46963     },
46964
46965 /**
46966  *      Utility method. Add a delete option to the DDView's context menu.
46967  *      @param {String} imageUrl The URL of the "delete" icon image.
46968  */
46969         setDeletable: function(imageUrl) {
46970                 if (!this.singleSelect && !this.multiSelect) {
46971                         this.singleSelect = true;
46972                 }
46973                 var c = this.getContextMenu();
46974                 this.contextMenu.on("itemclick", function(item) {
46975                         switch (item.id) {
46976                                 case "delete":
46977                                         this.remove(this.getSelectedIndexes());
46978                                         break;
46979                         }
46980                 }, this);
46981                 this.contextMenu.add({
46982                         icon: imageUrl,
46983                         id: "delete",
46984                         text: 'Delete'
46985                 });
46986         },
46987         
46988 /**     Return the context menu for this DDView. */
46989         getContextMenu: function() {
46990                 if (!this.contextMenu) {
46991 //                      Create the View's context menu
46992                         this.contextMenu = new Roo.menu.Menu({
46993                                 id: this.id + "-contextmenu"
46994                         });
46995                         this.el.on("contextmenu", this.showContextMenu, this);
46996                 }
46997                 return this.contextMenu;
46998         },
46999         
47000         disableContextMenu: function() {
47001                 if (this.contextMenu) {
47002                         this.el.un("contextmenu", this.showContextMenu, this);
47003                 }
47004         },
47005
47006         showContextMenu: function(e, item) {
47007         item = this.findItemFromChild(e.getTarget());
47008                 if (item) {
47009                         e.stopEvent();
47010                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
47011                         this.contextMenu.showAt(e.getXY());
47012             }
47013     },
47014
47015 /**
47016  *      Remove {@link Roo.data.Record}s at the specified indices.
47017  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
47018  */
47019     remove: function(selectedIndices) {
47020                 selectedIndices = [].concat(selectedIndices);
47021                 for (var i = 0; i < selectedIndices.length; i++) {
47022                         var rec = this.store.getAt(selectedIndices[i]);
47023                         this.store.remove(rec);
47024                 }
47025     },
47026
47027 /**
47028  *      Double click fires the event, but also, if this is draggable, and there is only one other
47029  *      related DropZone, it transfers the selected node.
47030  */
47031     onDblClick : function(e){
47032         var item = this.findItemFromChild(e.getTarget());
47033         if(item){
47034             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
47035                 return false;
47036             }
47037             if (this.dragGroup) {
47038                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
47039                     while (targets.indexOf(this.dropZone) > -1) {
47040                             targets.remove(this.dropZone);
47041                                 }
47042                     if (targets.length == 1) {
47043                                         this.dragZone.cachedTarget = null;
47044                         var el = Roo.get(targets[0].getEl());
47045                         var box = el.getBox(true);
47046                         targets[0].onNodeDrop(el.dom, {
47047                                 target: el.dom,
47048                                 xy: [box.x, box.y + box.height - 1]
47049                         }, null, this.getDragData(e));
47050                     }
47051                 }
47052         }
47053     },
47054     
47055     handleSelection: function(e) {
47056                 this.dragZone.cachedTarget = null;
47057         var item = this.findItemFromChild(e.getTarget());
47058         if (!item) {
47059                 this.clearSelections(true);
47060                 return;
47061         }
47062                 if (item && (this.multiSelect || this.singleSelect)){
47063                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
47064                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
47065                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
47066                                 this.unselect(item);
47067                         } else {
47068                                 this.select(item, this.multiSelect && e.ctrlKey);
47069                                 this.lastSelection = item;
47070                         }
47071                 }
47072     },
47073
47074     onItemClick : function(item, index, e){
47075                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
47076                         return false;
47077                 }
47078                 return true;
47079     },
47080
47081     unselect : function(nodeInfo, suppressEvent){
47082                 var node = this.getNode(nodeInfo);
47083                 if(node && this.isSelected(node)){
47084                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
47085                                 Roo.fly(node).removeClass(this.selectedClass);
47086                                 this.selections.remove(node);
47087                                 if(!suppressEvent){
47088                                         this.fireEvent("selectionchange", this, this.selections);
47089                                 }
47090                         }
47091                 }
47092     }
47093 });
47094 /*
47095  * Based on:
47096  * Ext JS Library 1.1.1
47097  * Copyright(c) 2006-2007, Ext JS, LLC.
47098  *
47099  * Originally Released Under LGPL - original licence link has changed is not relivant.
47100  *
47101  * Fork - LGPL
47102  * <script type="text/javascript">
47103  */
47104  
47105 /**
47106  * @class Roo.LayoutManager
47107  * @extends Roo.util.Observable
47108  * Base class for layout managers.
47109  */
47110 Roo.LayoutManager = function(container, config){
47111     Roo.LayoutManager.superclass.constructor.call(this);
47112     this.el = Roo.get(container);
47113     // ie scrollbar fix
47114     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
47115         document.body.scroll = "no";
47116     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
47117         this.el.position('relative');
47118     }
47119     this.id = this.el.id;
47120     this.el.addClass("x-layout-container");
47121     /** false to disable window resize monitoring @type Boolean */
47122     this.monitorWindowResize = true;
47123     this.regions = {};
47124     this.addEvents({
47125         /**
47126          * @event layout
47127          * Fires when a layout is performed. 
47128          * @param {Roo.LayoutManager} this
47129          */
47130         "layout" : true,
47131         /**
47132          * @event regionresized
47133          * Fires when the user resizes a region. 
47134          * @param {Roo.LayoutRegion} region The resized region
47135          * @param {Number} newSize The new size (width for east/west, height for north/south)
47136          */
47137         "regionresized" : true,
47138         /**
47139          * @event regioncollapsed
47140          * Fires when a region is collapsed. 
47141          * @param {Roo.LayoutRegion} region The collapsed region
47142          */
47143         "regioncollapsed" : true,
47144         /**
47145          * @event regionexpanded
47146          * Fires when a region is expanded.  
47147          * @param {Roo.LayoutRegion} region The expanded region
47148          */
47149         "regionexpanded" : true
47150     });
47151     this.updating = false;
47152     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47153 };
47154
47155 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
47156     /**
47157      * Returns true if this layout is currently being updated
47158      * @return {Boolean}
47159      */
47160     isUpdating : function(){
47161         return this.updating; 
47162     },
47163     
47164     /**
47165      * Suspend the LayoutManager from doing auto-layouts while
47166      * making multiple add or remove calls
47167      */
47168     beginUpdate : function(){
47169         this.updating = true;    
47170     },
47171     
47172     /**
47173      * Restore auto-layouts and optionally disable the manager from performing a layout
47174      * @param {Boolean} noLayout true to disable a layout update 
47175      */
47176     endUpdate : function(noLayout){
47177         this.updating = false;
47178         if(!noLayout){
47179             this.layout();
47180         }    
47181     },
47182     
47183     layout: function(){
47184         
47185     },
47186     
47187     onRegionResized : function(region, newSize){
47188         this.fireEvent("regionresized", region, newSize);
47189         this.layout();
47190     },
47191     
47192     onRegionCollapsed : function(region){
47193         this.fireEvent("regioncollapsed", region);
47194     },
47195     
47196     onRegionExpanded : function(region){
47197         this.fireEvent("regionexpanded", region);
47198     },
47199         
47200     /**
47201      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
47202      * performs box-model adjustments.
47203      * @return {Object} The size as an object {width: (the width), height: (the height)}
47204      */
47205     getViewSize : function(){
47206         var size;
47207         if(this.el.dom != document.body){
47208             size = this.el.getSize();
47209         }else{
47210             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
47211         }
47212         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
47213         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
47214         return size;
47215     },
47216     
47217     /**
47218      * Returns the Element this layout is bound to.
47219      * @return {Roo.Element}
47220      */
47221     getEl : function(){
47222         return this.el;
47223     },
47224     
47225     /**
47226      * Returns the specified region.
47227      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
47228      * @return {Roo.LayoutRegion}
47229      */
47230     getRegion : function(target){
47231         return this.regions[target.toLowerCase()];
47232     },
47233     
47234     onWindowResize : function(){
47235         if(this.monitorWindowResize){
47236             this.layout();
47237         }
47238     }
47239 });/*
47240  * Based on:
47241  * Ext JS Library 1.1.1
47242  * Copyright(c) 2006-2007, Ext JS, LLC.
47243  *
47244  * Originally Released Under LGPL - original licence link has changed is not relivant.
47245  *
47246  * Fork - LGPL
47247  * <script type="text/javascript">
47248  */
47249 /**
47250  * @class Roo.BorderLayout
47251  * @extends Roo.LayoutManager
47252  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
47253  * please see: <br><br>
47254  * <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>
47255  * <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>
47256  * Example:
47257  <pre><code>
47258  var layout = new Roo.BorderLayout(document.body, {
47259     north: {
47260         initialSize: 25,
47261         titlebar: false
47262     },
47263     west: {
47264         split:true,
47265         initialSize: 200,
47266         minSize: 175,
47267         maxSize: 400,
47268         titlebar: true,
47269         collapsible: true
47270     },
47271     east: {
47272         split:true,
47273         initialSize: 202,
47274         minSize: 175,
47275         maxSize: 400,
47276         titlebar: true,
47277         collapsible: true
47278     },
47279     south: {
47280         split:true,
47281         initialSize: 100,
47282         minSize: 100,
47283         maxSize: 200,
47284         titlebar: true,
47285         collapsible: true
47286     },
47287     center: {
47288         titlebar: true,
47289         autoScroll:true,
47290         resizeTabs: true,
47291         minTabWidth: 50,
47292         preferredTabWidth: 150
47293     }
47294 });
47295
47296 // shorthand
47297 var CP = Roo.ContentPanel;
47298
47299 layout.beginUpdate();
47300 layout.add("north", new CP("north", "North"));
47301 layout.add("south", new CP("south", {title: "South", closable: true}));
47302 layout.add("west", new CP("west", {title: "West"}));
47303 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
47304 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
47305 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
47306 layout.getRegion("center").showPanel("center1");
47307 layout.endUpdate();
47308 </code></pre>
47309
47310 <b>The container the layout is rendered into can be either the body element or any other element.
47311 If it is not the body element, the container needs to either be an absolute positioned element,
47312 or you will need to add "position:relative" to the css of the container.  You will also need to specify
47313 the container size if it is not the body element.</b>
47314
47315 * @constructor
47316 * Create a new BorderLayout
47317 * @param {String/HTMLElement/Element} container The container this layout is bound to
47318 * @param {Object} config Configuration options
47319  */
47320 Roo.BorderLayout = function(container, config){
47321     config = config || {};
47322     Roo.BorderLayout.superclass.constructor.call(this, container, config);
47323     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
47324     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
47325         var target = this.factory.validRegions[i];
47326         if(config[target]){
47327             this.addRegion(target, config[target]);
47328         }
47329     }
47330 };
47331
47332 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
47333     /**
47334      * Creates and adds a new region if it doesn't already exist.
47335      * @param {String} target The target region key (north, south, east, west or center).
47336      * @param {Object} config The regions config object
47337      * @return {BorderLayoutRegion} The new region
47338      */
47339     addRegion : function(target, config){
47340         if(!this.regions[target]){
47341             var r = this.factory.create(target, this, config);
47342             this.bindRegion(target, r);
47343         }
47344         return this.regions[target];
47345     },
47346
47347     // private (kinda)
47348     bindRegion : function(name, r){
47349         this.regions[name] = r;
47350         r.on("visibilitychange", this.layout, this);
47351         r.on("paneladded", this.layout, this);
47352         r.on("panelremoved", this.layout, this);
47353         r.on("invalidated", this.layout, this);
47354         r.on("resized", this.onRegionResized, this);
47355         r.on("collapsed", this.onRegionCollapsed, this);
47356         r.on("expanded", this.onRegionExpanded, this);
47357     },
47358
47359     /**
47360      * Performs a layout update.
47361      */
47362     layout : function(){
47363         if(this.updating) return;
47364         var size = this.getViewSize();
47365         var w = size.width;
47366         var h = size.height;
47367         var centerW = w;
47368         var centerH = h;
47369         var centerY = 0;
47370         var centerX = 0;
47371         //var x = 0, y = 0;
47372
47373         var rs = this.regions;
47374         var north = rs["north"];
47375         var south = rs["south"]; 
47376         var west = rs["west"];
47377         var east = rs["east"];
47378         var center = rs["center"];
47379         //if(this.hideOnLayout){ // not supported anymore
47380             //c.el.setStyle("display", "none");
47381         //}
47382         if(north && north.isVisible()){
47383             var b = north.getBox();
47384             var m = north.getMargins();
47385             b.width = w - (m.left+m.right);
47386             b.x = m.left;
47387             b.y = m.top;
47388             centerY = b.height + b.y + m.bottom;
47389             centerH -= centerY;
47390             north.updateBox(this.safeBox(b));
47391         }
47392         if(south && south.isVisible()){
47393             var b = south.getBox();
47394             var m = south.getMargins();
47395             b.width = w - (m.left+m.right);
47396             b.x = m.left;
47397             var totalHeight = (b.height + m.top + m.bottom);
47398             b.y = h - totalHeight + m.top;
47399             centerH -= totalHeight;
47400             south.updateBox(this.safeBox(b));
47401         }
47402         if(west && west.isVisible()){
47403             var b = west.getBox();
47404             var m = west.getMargins();
47405             b.height = centerH - (m.top+m.bottom);
47406             b.x = m.left;
47407             b.y = centerY + m.top;
47408             var totalWidth = (b.width + m.left + m.right);
47409             centerX += totalWidth;
47410             centerW -= totalWidth;
47411             west.updateBox(this.safeBox(b));
47412         }
47413         if(east && east.isVisible()){
47414             var b = east.getBox();
47415             var m = east.getMargins();
47416             b.height = centerH - (m.top+m.bottom);
47417             var totalWidth = (b.width + m.left + m.right);
47418             b.x = w - totalWidth + m.left;
47419             b.y = centerY + m.top;
47420             centerW -= totalWidth;
47421             east.updateBox(this.safeBox(b));
47422         }
47423         if(center){
47424             var m = center.getMargins();
47425             var centerBox = {
47426                 x: centerX + m.left,
47427                 y: centerY + m.top,
47428                 width: centerW - (m.left+m.right),
47429                 height: centerH - (m.top+m.bottom)
47430             };
47431             //if(this.hideOnLayout){
47432                 //center.el.setStyle("display", "block");
47433             //}
47434             center.updateBox(this.safeBox(centerBox));
47435         }
47436         this.el.repaint();
47437         this.fireEvent("layout", this);
47438     },
47439
47440     // private
47441     safeBox : function(box){
47442         box.width = Math.max(0, box.width);
47443         box.height = Math.max(0, box.height);
47444         return box;
47445     },
47446
47447     /**
47448      * Adds a ContentPanel (or subclass) to this layout.
47449      * @param {String} target The target region key (north, south, east, west or center).
47450      * @param {Roo.ContentPanel} panel The panel to add
47451      * @return {Roo.ContentPanel} The added panel
47452      */
47453     add : function(target, panel){
47454          
47455         target = target.toLowerCase();
47456         return this.regions[target].add(panel);
47457     },
47458
47459     /**
47460      * Remove a ContentPanel (or subclass) to this layout.
47461      * @param {String} target The target region key (north, south, east, west or center).
47462      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
47463      * @return {Roo.ContentPanel} The removed panel
47464      */
47465     remove : function(target, panel){
47466         target = target.toLowerCase();
47467         return this.regions[target].remove(panel);
47468     },
47469
47470     /**
47471      * Searches all regions for a panel with the specified id
47472      * @param {String} panelId
47473      * @return {Roo.ContentPanel} The panel or null if it wasn't found
47474      */
47475     findPanel : function(panelId){
47476         var rs = this.regions;
47477         for(var target in rs){
47478             if(typeof rs[target] != "function"){
47479                 var p = rs[target].getPanel(panelId);
47480                 if(p){
47481                     return p;
47482                 }
47483             }
47484         }
47485         return null;
47486     },
47487
47488     /**
47489      * Searches all regions for a panel with the specified id and activates (shows) it.
47490      * @param {String/ContentPanel} panelId The panels id or the panel itself
47491      * @return {Roo.ContentPanel} The shown panel or null
47492      */
47493     showPanel : function(panelId) {
47494       var rs = this.regions;
47495       for(var target in rs){
47496          var r = rs[target];
47497          if(typeof r != "function"){
47498             if(r.hasPanel(panelId)){
47499                return r.showPanel(panelId);
47500             }
47501          }
47502       }
47503       return null;
47504    },
47505
47506    /**
47507      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
47508      * @param {Roo.state.Provider} provider (optional) An alternate state provider
47509      */
47510     restoreState : function(provider){
47511         if(!provider){
47512             provider = Roo.state.Manager;
47513         }
47514         var sm = new Roo.LayoutStateManager();
47515         sm.init(this, provider);
47516     },
47517
47518     /**
47519      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
47520      * object should contain properties for each region to add ContentPanels to, and each property's value should be
47521      * a valid ContentPanel config object.  Example:
47522      * <pre><code>
47523 // Create the main layout
47524 var layout = new Roo.BorderLayout('main-ct', {
47525     west: {
47526         split:true,
47527         minSize: 175,
47528         titlebar: true
47529     },
47530     center: {
47531         title:'Components'
47532     }
47533 }, 'main-ct');
47534
47535 // Create and add multiple ContentPanels at once via configs
47536 layout.batchAdd({
47537    west: {
47538        id: 'source-files',
47539        autoCreate:true,
47540        title:'Ext Source Files',
47541        autoScroll:true,
47542        fitToFrame:true
47543    },
47544    center : {
47545        el: cview,
47546        autoScroll:true,
47547        fitToFrame:true,
47548        toolbar: tb,
47549        resizeEl:'cbody'
47550    }
47551 });
47552 </code></pre>
47553      * @param {Object} regions An object containing ContentPanel configs by region name
47554      */
47555     batchAdd : function(regions){
47556         this.beginUpdate();
47557         for(var rname in regions){
47558             var lr = this.regions[rname];
47559             if(lr){
47560                 this.addTypedPanels(lr, regions[rname]);
47561             }
47562         }
47563         this.endUpdate();
47564     },
47565
47566     // private
47567     addTypedPanels : function(lr, ps){
47568         if(typeof ps == 'string'){
47569             lr.add(new Roo.ContentPanel(ps));
47570         }
47571         else if(ps instanceof Array){
47572             for(var i =0, len = ps.length; i < len; i++){
47573                 this.addTypedPanels(lr, ps[i]);
47574             }
47575         }
47576         else if(!ps.events){ // raw config?
47577             var el = ps.el;
47578             delete ps.el; // prevent conflict
47579             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
47580         }
47581         else {  // panel object assumed!
47582             lr.add(ps);
47583         }
47584     },
47585     /**
47586      * Adds a xtype elements to the layout.
47587      * <pre><code>
47588
47589 layout.addxtype({
47590        xtype : 'ContentPanel',
47591        region: 'west',
47592        items: [ .... ]
47593    }
47594 );
47595
47596 layout.addxtype({
47597         xtype : 'NestedLayoutPanel',
47598         region: 'west',
47599         layout: {
47600            center: { },
47601            west: { }   
47602         },
47603         items : [ ... list of content panels or nested layout panels.. ]
47604    }
47605 );
47606 </code></pre>
47607      * @param {Object} cfg Xtype definition of item to add.
47608      */
47609     addxtype : function(cfg)
47610     {
47611         // basically accepts a pannel...
47612         // can accept a layout region..!?!?
47613         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
47614         
47615         if (!cfg.xtype.match(/Panel$/)) {
47616             return false;
47617         }
47618         var ret = false;
47619         
47620         if (typeof(cfg.region) == 'undefined') {
47621             Roo.log("Failed to add Panel, region was not set");
47622             Roo.log(cfg);
47623             return false;
47624         }
47625         var region = cfg.region;
47626         delete cfg.region;
47627         
47628           
47629         var xitems = [];
47630         if (cfg.items) {
47631             xitems = cfg.items;
47632             delete cfg.items;
47633         }
47634         var nb = false;
47635         
47636         switch(cfg.xtype) 
47637         {
47638             case 'ContentPanel':  // ContentPanel (el, cfg)
47639             case 'ScrollPanel':  // ContentPanel (el, cfg)
47640             case 'ViewPanel': 
47641                 if(cfg.autoCreate) {
47642                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
47643                 } else {
47644                     var el = this.el.createChild();
47645                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
47646                 }
47647                 
47648                 this.add(region, ret);
47649                 break;
47650             
47651             
47652             case 'TreePanel': // our new panel!
47653                 cfg.el = this.el.createChild();
47654                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
47655                 this.add(region, ret);
47656                 break;
47657             
47658             case 'NestedLayoutPanel': 
47659                 // create a new Layout (which is  a Border Layout...
47660                 var el = this.el.createChild();
47661                 var clayout = cfg.layout;
47662                 delete cfg.layout;
47663                 clayout.items   = clayout.items  || [];
47664                 // replace this exitems with the clayout ones..
47665                 xitems = clayout.items;
47666                  
47667                 
47668                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
47669                     cfg.background = false;
47670                 }
47671                 var layout = new Roo.BorderLayout(el, clayout);
47672                 
47673                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
47674                 //console.log('adding nested layout panel '  + cfg.toSource());
47675                 this.add(region, ret);
47676                 nb = {}; /// find first...
47677                 break;
47678                 
47679             case 'GridPanel': 
47680             
47681                 // needs grid and region
47682                 
47683                 //var el = this.getRegion(region).el.createChild();
47684                 var el = this.el.createChild();
47685                 // create the grid first...
47686                 
47687                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
47688                 delete cfg.grid;
47689                 if (region == 'center' && this.active ) {
47690                     cfg.background = false;
47691                 }
47692                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
47693                 
47694                 this.add(region, ret);
47695                 if (cfg.background) {
47696                     ret.on('activate', function(gp) {
47697                         if (!gp.grid.rendered) {
47698                             gp.grid.render();
47699                         }
47700                     });
47701                 } else {
47702                     grid.render();
47703                 }
47704                 break;
47705            
47706            
47707            
47708                 
47709                 
47710                 
47711             default: 
47712                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
47713                 return null;
47714              // GridPanel (grid, cfg)
47715             
47716         }
47717         this.beginUpdate();
47718         // add children..
47719         var region = '';
47720         var abn = {};
47721         Roo.each(xitems, function(i)  {
47722             region = nb && i.region ? i.region : false;
47723             
47724             var add = ret.addxtype(i);
47725            
47726             if (region) {
47727                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
47728                 if (!i.background) {
47729                     abn[region] = nb[region] ;
47730                 }
47731             }
47732             
47733         });
47734         this.endUpdate();
47735
47736         // make the last non-background panel active..
47737         //if (nb) { Roo.log(abn); }
47738         if (nb) {
47739             
47740             for(var r in abn) {
47741                 region = this.getRegion(r);
47742                 if (region) {
47743                     // tried using nb[r], but it does not work..
47744                      
47745                     region.showPanel(abn[r]);
47746                    
47747                 }
47748             }
47749         }
47750         return ret;
47751         
47752     }
47753 });
47754
47755 /**
47756  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
47757  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
47758  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
47759  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
47760  * <pre><code>
47761 // shorthand
47762 var CP = Roo.ContentPanel;
47763
47764 var layout = Roo.BorderLayout.create({
47765     north: {
47766         initialSize: 25,
47767         titlebar: false,
47768         panels: [new CP("north", "North")]
47769     },
47770     west: {
47771         split:true,
47772         initialSize: 200,
47773         minSize: 175,
47774         maxSize: 400,
47775         titlebar: true,
47776         collapsible: true,
47777         panels: [new CP("west", {title: "West"})]
47778     },
47779     east: {
47780         split:true,
47781         initialSize: 202,
47782         minSize: 175,
47783         maxSize: 400,
47784         titlebar: true,
47785         collapsible: true,
47786         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
47787     },
47788     south: {
47789         split:true,
47790         initialSize: 100,
47791         minSize: 100,
47792         maxSize: 200,
47793         titlebar: true,
47794         collapsible: true,
47795         panels: [new CP("south", {title: "South", closable: true})]
47796     },
47797     center: {
47798         titlebar: true,
47799         autoScroll:true,
47800         resizeTabs: true,
47801         minTabWidth: 50,
47802         preferredTabWidth: 150,
47803         panels: [
47804             new CP("center1", {title: "Close Me", closable: true}),
47805             new CP("center2", {title: "Center Panel", closable: false})
47806         ]
47807     }
47808 }, document.body);
47809
47810 layout.getRegion("center").showPanel("center1");
47811 </code></pre>
47812  * @param config
47813  * @param targetEl
47814  */
47815 Roo.BorderLayout.create = function(config, targetEl){
47816     var layout = new Roo.BorderLayout(targetEl || document.body, config);
47817     layout.beginUpdate();
47818     var regions = Roo.BorderLayout.RegionFactory.validRegions;
47819     for(var j = 0, jlen = regions.length; j < jlen; j++){
47820         var lr = regions[j];
47821         if(layout.regions[lr] && config[lr].panels){
47822             var r = layout.regions[lr];
47823             var ps = config[lr].panels;
47824             layout.addTypedPanels(r, ps);
47825         }
47826     }
47827     layout.endUpdate();
47828     return layout;
47829 };
47830
47831 // private
47832 Roo.BorderLayout.RegionFactory = {
47833     // private
47834     validRegions : ["north","south","east","west","center"],
47835
47836     // private
47837     create : function(target, mgr, config){
47838         target = target.toLowerCase();
47839         if(config.lightweight || config.basic){
47840             return new Roo.BasicLayoutRegion(mgr, config, target);
47841         }
47842         switch(target){
47843             case "north":
47844                 return new Roo.NorthLayoutRegion(mgr, config);
47845             case "south":
47846                 return new Roo.SouthLayoutRegion(mgr, config);
47847             case "east":
47848                 return new Roo.EastLayoutRegion(mgr, config);
47849             case "west":
47850                 return new Roo.WestLayoutRegion(mgr, config);
47851             case "center":
47852                 return new Roo.CenterLayoutRegion(mgr, config);
47853         }
47854         throw 'Layout region "'+target+'" not supported.';
47855     }
47856 };/*
47857  * Based on:
47858  * Ext JS Library 1.1.1
47859  * Copyright(c) 2006-2007, Ext JS, LLC.
47860  *
47861  * Originally Released Under LGPL - original licence link has changed is not relivant.
47862  *
47863  * Fork - LGPL
47864  * <script type="text/javascript">
47865  */
47866  
47867 /**
47868  * @class Roo.BasicLayoutRegion
47869  * @extends Roo.util.Observable
47870  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
47871  * and does not have a titlebar, tabs or any other features. All it does is size and position 
47872  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
47873  */
47874 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
47875     this.mgr = mgr;
47876     this.position  = pos;
47877     this.events = {
47878         /**
47879          * @scope Roo.BasicLayoutRegion
47880          */
47881         
47882         /**
47883          * @event beforeremove
47884          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
47885          * @param {Roo.LayoutRegion} this
47886          * @param {Roo.ContentPanel} panel The panel
47887          * @param {Object} e The cancel event object
47888          */
47889         "beforeremove" : true,
47890         /**
47891          * @event invalidated
47892          * Fires when the layout for this region is changed.
47893          * @param {Roo.LayoutRegion} this
47894          */
47895         "invalidated" : true,
47896         /**
47897          * @event visibilitychange
47898          * Fires when this region is shown or hidden 
47899          * @param {Roo.LayoutRegion} this
47900          * @param {Boolean} visibility true or false
47901          */
47902         "visibilitychange" : true,
47903         /**
47904          * @event paneladded
47905          * Fires when a panel is added. 
47906          * @param {Roo.LayoutRegion} this
47907          * @param {Roo.ContentPanel} panel The panel
47908          */
47909         "paneladded" : true,
47910         /**
47911          * @event panelremoved
47912          * Fires when a panel is removed. 
47913          * @param {Roo.LayoutRegion} this
47914          * @param {Roo.ContentPanel} panel The panel
47915          */
47916         "panelremoved" : true,
47917         /**
47918          * @event collapsed
47919          * Fires when this region is collapsed.
47920          * @param {Roo.LayoutRegion} this
47921          */
47922         "collapsed" : true,
47923         /**
47924          * @event expanded
47925          * Fires when this region is expanded.
47926          * @param {Roo.LayoutRegion} this
47927          */
47928         "expanded" : true,
47929         /**
47930          * @event slideshow
47931          * Fires when this region is slid into view.
47932          * @param {Roo.LayoutRegion} this
47933          */
47934         "slideshow" : true,
47935         /**
47936          * @event slidehide
47937          * Fires when this region slides out of view. 
47938          * @param {Roo.LayoutRegion} this
47939          */
47940         "slidehide" : true,
47941         /**
47942          * @event panelactivated
47943          * Fires when a panel is activated. 
47944          * @param {Roo.LayoutRegion} this
47945          * @param {Roo.ContentPanel} panel The activated panel
47946          */
47947         "panelactivated" : true,
47948         /**
47949          * @event resized
47950          * Fires when the user resizes this region. 
47951          * @param {Roo.LayoutRegion} this
47952          * @param {Number} newSize The new size (width for east/west, height for north/south)
47953          */
47954         "resized" : true
47955     };
47956     /** A collection of panels in this region. @type Roo.util.MixedCollection */
47957     this.panels = new Roo.util.MixedCollection();
47958     this.panels.getKey = this.getPanelId.createDelegate(this);
47959     this.box = null;
47960     this.activePanel = null;
47961     // ensure listeners are added...
47962     
47963     if (config.listeners || config.events) {
47964         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
47965             listeners : config.listeners || {},
47966             events : config.events || {}
47967         });
47968     }
47969     
47970     if(skipConfig !== true){
47971         this.applyConfig(config);
47972     }
47973 };
47974
47975 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
47976     getPanelId : function(p){
47977         return p.getId();
47978     },
47979     
47980     applyConfig : function(config){
47981         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
47982         this.config = config;
47983         
47984     },
47985     
47986     /**
47987      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
47988      * the width, for horizontal (north, south) the height.
47989      * @param {Number} newSize The new width or height
47990      */
47991     resizeTo : function(newSize){
47992         var el = this.el ? this.el :
47993                  (this.activePanel ? this.activePanel.getEl() : null);
47994         if(el){
47995             switch(this.position){
47996                 case "east":
47997                 case "west":
47998                     el.setWidth(newSize);
47999                     this.fireEvent("resized", this, newSize);
48000                 break;
48001                 case "north":
48002                 case "south":
48003                     el.setHeight(newSize);
48004                     this.fireEvent("resized", this, newSize);
48005                 break;                
48006             }
48007         }
48008     },
48009     
48010     getBox : function(){
48011         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
48012     },
48013     
48014     getMargins : function(){
48015         return this.margins;
48016     },
48017     
48018     updateBox : function(box){
48019         this.box = box;
48020         var el = this.activePanel.getEl();
48021         el.dom.style.left = box.x + "px";
48022         el.dom.style.top = box.y + "px";
48023         this.activePanel.setSize(box.width, box.height);
48024     },
48025     
48026     /**
48027      * Returns the container element for this region.
48028      * @return {Roo.Element}
48029      */
48030     getEl : function(){
48031         return this.activePanel;
48032     },
48033     
48034     /**
48035      * Returns true if this region is currently visible.
48036      * @return {Boolean}
48037      */
48038     isVisible : function(){
48039         return this.activePanel ? true : false;
48040     },
48041     
48042     setActivePanel : function(panel){
48043         panel = this.getPanel(panel);
48044         if(this.activePanel && this.activePanel != panel){
48045             this.activePanel.setActiveState(false);
48046             this.activePanel.getEl().setLeftTop(-10000,-10000);
48047         }
48048         this.activePanel = panel;
48049         panel.setActiveState(true);
48050         if(this.box){
48051             panel.setSize(this.box.width, this.box.height);
48052         }
48053         this.fireEvent("panelactivated", this, panel);
48054         this.fireEvent("invalidated");
48055     },
48056     
48057     /**
48058      * Show the specified panel.
48059      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
48060      * @return {Roo.ContentPanel} The shown panel or null
48061      */
48062     showPanel : function(panel){
48063         if(panel = this.getPanel(panel)){
48064             this.setActivePanel(panel);
48065         }
48066         return panel;
48067     },
48068     
48069     /**
48070      * Get the active panel for this region.
48071      * @return {Roo.ContentPanel} The active panel or null
48072      */
48073     getActivePanel : function(){
48074         return this.activePanel;
48075     },
48076     
48077     /**
48078      * Add the passed ContentPanel(s)
48079      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
48080      * @return {Roo.ContentPanel} The panel added (if only one was added)
48081      */
48082     add : function(panel){
48083         if(arguments.length > 1){
48084             for(var i = 0, len = arguments.length; i < len; i++) {
48085                 this.add(arguments[i]);
48086             }
48087             return null;
48088         }
48089         if(this.hasPanel(panel)){
48090             this.showPanel(panel);
48091             return panel;
48092         }
48093         var el = panel.getEl();
48094         if(el.dom.parentNode != this.mgr.el.dom){
48095             this.mgr.el.dom.appendChild(el.dom);
48096         }
48097         if(panel.setRegion){
48098             panel.setRegion(this);
48099         }
48100         this.panels.add(panel);
48101         el.setStyle("position", "absolute");
48102         if(!panel.background){
48103             this.setActivePanel(panel);
48104             if(this.config.initialSize && this.panels.getCount()==1){
48105                 this.resizeTo(this.config.initialSize);
48106             }
48107         }
48108         this.fireEvent("paneladded", this, panel);
48109         return panel;
48110     },
48111     
48112     /**
48113      * Returns true if the panel is in this region.
48114      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48115      * @return {Boolean}
48116      */
48117     hasPanel : function(panel){
48118         if(typeof panel == "object"){ // must be panel obj
48119             panel = panel.getId();
48120         }
48121         return this.getPanel(panel) ? true : false;
48122     },
48123     
48124     /**
48125      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
48126      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48127      * @param {Boolean} preservePanel Overrides the config preservePanel option
48128      * @return {Roo.ContentPanel} The panel that was removed
48129      */
48130     remove : function(panel, preservePanel){
48131         panel = this.getPanel(panel);
48132         if(!panel){
48133             return null;
48134         }
48135         var e = {};
48136         this.fireEvent("beforeremove", this, panel, e);
48137         if(e.cancel === true){
48138             return null;
48139         }
48140         var panelId = panel.getId();
48141         this.panels.removeKey(panelId);
48142         return panel;
48143     },
48144     
48145     /**
48146      * Returns the panel specified or null if it's not in this region.
48147      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48148      * @return {Roo.ContentPanel}
48149      */
48150     getPanel : function(id){
48151         if(typeof id == "object"){ // must be panel obj
48152             return id;
48153         }
48154         return this.panels.get(id);
48155     },
48156     
48157     /**
48158      * Returns this regions position (north/south/east/west/center).
48159      * @return {String} 
48160      */
48161     getPosition: function(){
48162         return this.position;    
48163     }
48164 });/*
48165  * Based on:
48166  * Ext JS Library 1.1.1
48167  * Copyright(c) 2006-2007, Ext JS, LLC.
48168  *
48169  * Originally Released Under LGPL - original licence link has changed is not relivant.
48170  *
48171  * Fork - LGPL
48172  * <script type="text/javascript">
48173  */
48174  
48175 /**
48176  * @class Roo.LayoutRegion
48177  * @extends Roo.BasicLayoutRegion
48178  * This class represents a region in a layout manager.
48179  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
48180  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
48181  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
48182  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
48183  * @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})
48184  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
48185  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
48186  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
48187  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
48188  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
48189  * @cfg {String}    title           The title for the region (overrides panel titles)
48190  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
48191  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
48192  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
48193  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
48194  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
48195  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
48196  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
48197  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
48198  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
48199  * @cfg {Boolean}   showPin         True to show a pin button
48200  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
48201  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
48202  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
48203  * @cfg {Number}    width           For East/West panels
48204  * @cfg {Number}    height          For North/South panels
48205  * @cfg {Boolean}   split           To show the splitter
48206  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
48207  */
48208 Roo.LayoutRegion = function(mgr, config, pos){
48209     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
48210     var dh = Roo.DomHelper;
48211     /** This region's container element 
48212     * @type Roo.Element */
48213     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
48214     /** This region's title element 
48215     * @type Roo.Element */
48216
48217     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
48218         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
48219         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
48220     ]}, true);
48221     this.titleEl.enableDisplayMode();
48222     /** This region's title text element 
48223     * @type HTMLElement */
48224     this.titleTextEl = this.titleEl.dom.firstChild;
48225     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
48226     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
48227     this.closeBtn.enableDisplayMode();
48228     this.closeBtn.on("click", this.closeClicked, this);
48229     this.closeBtn.hide();
48230
48231     this.createBody(config);
48232     this.visible = true;
48233     this.collapsed = false;
48234
48235     if(config.hideWhenEmpty){
48236         this.hide();
48237         this.on("paneladded", this.validateVisibility, this);
48238         this.on("panelremoved", this.validateVisibility, this);
48239     }
48240     this.applyConfig(config);
48241 };
48242
48243 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
48244
48245     createBody : function(){
48246         /** This region's body element 
48247         * @type Roo.Element */
48248         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
48249     },
48250
48251     applyConfig : function(c){
48252         if(c.collapsible && this.position != "center" && !this.collapsedEl){
48253             var dh = Roo.DomHelper;
48254             if(c.titlebar !== false){
48255                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
48256                 this.collapseBtn.on("click", this.collapse, this);
48257                 this.collapseBtn.enableDisplayMode();
48258
48259                 if(c.showPin === true || this.showPin){
48260                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
48261                     this.stickBtn.enableDisplayMode();
48262                     this.stickBtn.on("click", this.expand, this);
48263                     this.stickBtn.hide();
48264                 }
48265             }
48266             /** This region's collapsed element
48267             * @type Roo.Element */
48268             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
48269                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
48270             ]}, true);
48271             if(c.floatable !== false){
48272                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
48273                this.collapsedEl.on("click", this.collapseClick, this);
48274             }
48275
48276             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
48277                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
48278                    id: "message", unselectable: "on", style:{"float":"left"}});
48279                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
48280              }
48281             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
48282             this.expandBtn.on("click", this.expand, this);
48283         }
48284         if(this.collapseBtn){
48285             this.collapseBtn.setVisible(c.collapsible == true);
48286         }
48287         this.cmargins = c.cmargins || this.cmargins ||
48288                          (this.position == "west" || this.position == "east" ?
48289                              {top: 0, left: 2, right:2, bottom: 0} :
48290                              {top: 2, left: 0, right:0, bottom: 2});
48291         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48292         this.bottomTabs = c.tabPosition != "top";
48293         this.autoScroll = c.autoScroll || false;
48294         if(this.autoScroll){
48295             this.bodyEl.setStyle("overflow", "auto");
48296         }else{
48297             this.bodyEl.setStyle("overflow", "hidden");
48298         }
48299         //if(c.titlebar !== false){
48300             if((!c.titlebar && !c.title) || c.titlebar === false){
48301                 this.titleEl.hide();
48302             }else{
48303                 this.titleEl.show();
48304                 if(c.title){
48305                     this.titleTextEl.innerHTML = c.title;
48306                 }
48307             }
48308         //}
48309         this.duration = c.duration || .30;
48310         this.slideDuration = c.slideDuration || .45;
48311         this.config = c;
48312         if(c.collapsed){
48313             this.collapse(true);
48314         }
48315         if(c.hidden){
48316             this.hide();
48317         }
48318     },
48319     /**
48320      * Returns true if this region is currently visible.
48321      * @return {Boolean}
48322      */
48323     isVisible : function(){
48324         return this.visible;
48325     },
48326
48327     /**
48328      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
48329      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
48330      */
48331     setCollapsedTitle : function(title){
48332         title = title || "&#160;";
48333         if(this.collapsedTitleTextEl){
48334             this.collapsedTitleTextEl.innerHTML = title;
48335         }
48336     },
48337
48338     getBox : function(){
48339         var b;
48340         if(!this.collapsed){
48341             b = this.el.getBox(false, true);
48342         }else{
48343             b = this.collapsedEl.getBox(false, true);
48344         }
48345         return b;
48346     },
48347
48348     getMargins : function(){
48349         return this.collapsed ? this.cmargins : this.margins;
48350     },
48351
48352     highlight : function(){
48353         this.el.addClass("x-layout-panel-dragover");
48354     },
48355
48356     unhighlight : function(){
48357         this.el.removeClass("x-layout-panel-dragover");
48358     },
48359
48360     updateBox : function(box){
48361         this.box = box;
48362         if(!this.collapsed){
48363             this.el.dom.style.left = box.x + "px";
48364             this.el.dom.style.top = box.y + "px";
48365             this.updateBody(box.width, box.height);
48366         }else{
48367             this.collapsedEl.dom.style.left = box.x + "px";
48368             this.collapsedEl.dom.style.top = box.y + "px";
48369             this.collapsedEl.setSize(box.width, box.height);
48370         }
48371         if(this.tabs){
48372             this.tabs.autoSizeTabs();
48373         }
48374     },
48375
48376     updateBody : function(w, h){
48377         if(w !== null){
48378             this.el.setWidth(w);
48379             w -= this.el.getBorderWidth("rl");
48380             if(this.config.adjustments){
48381                 w += this.config.adjustments[0];
48382             }
48383         }
48384         if(h !== null){
48385             this.el.setHeight(h);
48386             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
48387             h -= this.el.getBorderWidth("tb");
48388             if(this.config.adjustments){
48389                 h += this.config.adjustments[1];
48390             }
48391             this.bodyEl.setHeight(h);
48392             if(this.tabs){
48393                 h = this.tabs.syncHeight(h);
48394             }
48395         }
48396         if(this.panelSize){
48397             w = w !== null ? w : this.panelSize.width;
48398             h = h !== null ? h : this.panelSize.height;
48399         }
48400         if(this.activePanel){
48401             var el = this.activePanel.getEl();
48402             w = w !== null ? w : el.getWidth();
48403             h = h !== null ? h : el.getHeight();
48404             this.panelSize = {width: w, height: h};
48405             this.activePanel.setSize(w, h);
48406         }
48407         if(Roo.isIE && this.tabs){
48408             this.tabs.el.repaint();
48409         }
48410     },
48411
48412     /**
48413      * Returns the container element for this region.
48414      * @return {Roo.Element}
48415      */
48416     getEl : function(){
48417         return this.el;
48418     },
48419
48420     /**
48421      * Hides this region.
48422      */
48423     hide : function(){
48424         if(!this.collapsed){
48425             this.el.dom.style.left = "-2000px";
48426             this.el.hide();
48427         }else{
48428             this.collapsedEl.dom.style.left = "-2000px";
48429             this.collapsedEl.hide();
48430         }
48431         this.visible = false;
48432         this.fireEvent("visibilitychange", this, false);
48433     },
48434
48435     /**
48436      * Shows this region if it was previously hidden.
48437      */
48438     show : function(){
48439         if(!this.collapsed){
48440             this.el.show();
48441         }else{
48442             this.collapsedEl.show();
48443         }
48444         this.visible = true;
48445         this.fireEvent("visibilitychange", this, true);
48446     },
48447
48448     closeClicked : function(){
48449         if(this.activePanel){
48450             this.remove(this.activePanel);
48451         }
48452     },
48453
48454     collapseClick : function(e){
48455         if(this.isSlid){
48456            e.stopPropagation();
48457            this.slideIn();
48458         }else{
48459            e.stopPropagation();
48460            this.slideOut();
48461         }
48462     },
48463
48464     /**
48465      * Collapses this region.
48466      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
48467      */
48468     collapse : function(skipAnim){
48469         if(this.collapsed) return;
48470         this.collapsed = true;
48471         if(this.split){
48472             this.split.el.hide();
48473         }
48474         if(this.config.animate && skipAnim !== true){
48475             this.fireEvent("invalidated", this);
48476             this.animateCollapse();
48477         }else{
48478             this.el.setLocation(-20000,-20000);
48479             this.el.hide();
48480             this.collapsedEl.show();
48481             this.fireEvent("collapsed", this);
48482             this.fireEvent("invalidated", this);
48483         }
48484     },
48485
48486     animateCollapse : function(){
48487         // overridden
48488     },
48489
48490     /**
48491      * Expands this region if it was previously collapsed.
48492      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
48493      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
48494      */
48495     expand : function(e, skipAnim){
48496         if(e) e.stopPropagation();
48497         if(!this.collapsed || this.el.hasActiveFx()) return;
48498         if(this.isSlid){
48499             this.afterSlideIn();
48500             skipAnim = true;
48501         }
48502         this.collapsed = false;
48503         if(this.config.animate && skipAnim !== true){
48504             this.animateExpand();
48505         }else{
48506             this.el.show();
48507             if(this.split){
48508                 this.split.el.show();
48509             }
48510             this.collapsedEl.setLocation(-2000,-2000);
48511             this.collapsedEl.hide();
48512             this.fireEvent("invalidated", this);
48513             this.fireEvent("expanded", this);
48514         }
48515     },
48516
48517     animateExpand : function(){
48518         // overridden
48519     },
48520
48521     initTabs : function()
48522     {
48523         this.bodyEl.setStyle("overflow", "hidden");
48524         var ts = new Roo.TabPanel(
48525                 this.bodyEl.dom,
48526                 {
48527                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
48528                     disableTooltips: this.config.disableTabTips,
48529                     toolbar : this.config.toolbar
48530                 }
48531         );
48532         if(this.config.hideTabs){
48533             ts.stripWrap.setDisplayed(false);
48534         }
48535         this.tabs = ts;
48536         ts.resizeTabs = this.config.resizeTabs === true;
48537         ts.minTabWidth = this.config.minTabWidth || 40;
48538         ts.maxTabWidth = this.config.maxTabWidth || 250;
48539         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
48540         ts.monitorResize = false;
48541         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
48542         ts.bodyEl.addClass('x-layout-tabs-body');
48543         this.panels.each(this.initPanelAsTab, this);
48544     },
48545
48546     initPanelAsTab : function(panel){
48547         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
48548                     this.config.closeOnTab && panel.isClosable());
48549         if(panel.tabTip !== undefined){
48550             ti.setTooltip(panel.tabTip);
48551         }
48552         ti.on("activate", function(){
48553               this.setActivePanel(panel);
48554         }, this);
48555         if(this.config.closeOnTab){
48556             ti.on("beforeclose", function(t, e){
48557                 e.cancel = true;
48558                 this.remove(panel);
48559             }, this);
48560         }
48561         return ti;
48562     },
48563
48564     updatePanelTitle : function(panel, title){
48565         if(this.activePanel == panel){
48566             this.updateTitle(title);
48567         }
48568         if(this.tabs){
48569             var ti = this.tabs.getTab(panel.getEl().id);
48570             ti.setText(title);
48571             if(panel.tabTip !== undefined){
48572                 ti.setTooltip(panel.tabTip);
48573             }
48574         }
48575     },
48576
48577     updateTitle : function(title){
48578         if(this.titleTextEl && !this.config.title){
48579             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
48580         }
48581     },
48582
48583     setActivePanel : function(panel){
48584         panel = this.getPanel(panel);
48585         if(this.activePanel && this.activePanel != panel){
48586             this.activePanel.setActiveState(false);
48587         }
48588         this.activePanel = panel;
48589         panel.setActiveState(true);
48590         if(this.panelSize){
48591             panel.setSize(this.panelSize.width, this.panelSize.height);
48592         }
48593         if(this.closeBtn){
48594             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
48595         }
48596         this.updateTitle(panel.getTitle());
48597         if(this.tabs){
48598             this.fireEvent("invalidated", this);
48599         }
48600         this.fireEvent("panelactivated", this, panel);
48601     },
48602
48603     /**
48604      * Shows the specified panel.
48605      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
48606      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
48607      */
48608     showPanel : function(panel){
48609         if(panel = this.getPanel(panel)){
48610             if(this.tabs){
48611                 var tab = this.tabs.getTab(panel.getEl().id);
48612                 if(tab.isHidden()){
48613                     this.tabs.unhideTab(tab.id);
48614                 }
48615                 tab.activate();
48616             }else{
48617                 this.setActivePanel(panel);
48618             }
48619         }
48620         return panel;
48621     },
48622
48623     /**
48624      * Get the active panel for this region.
48625      * @return {Roo.ContentPanel} The active panel or null
48626      */
48627     getActivePanel : function(){
48628         return this.activePanel;
48629     },
48630
48631     validateVisibility : function(){
48632         if(this.panels.getCount() < 1){
48633             this.updateTitle("&#160;");
48634             this.closeBtn.hide();
48635             this.hide();
48636         }else{
48637             if(!this.isVisible()){
48638                 this.show();
48639             }
48640         }
48641     },
48642
48643     /**
48644      * Adds the passed ContentPanel(s) to this region.
48645      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
48646      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
48647      */
48648     add : function(panel){
48649         if(arguments.length > 1){
48650             for(var i = 0, len = arguments.length; i < len; i++) {
48651                 this.add(arguments[i]);
48652             }
48653             return null;
48654         }
48655         if(this.hasPanel(panel)){
48656             this.showPanel(panel);
48657             return panel;
48658         }
48659         panel.setRegion(this);
48660         this.panels.add(panel);
48661         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
48662             this.bodyEl.dom.appendChild(panel.getEl().dom);
48663             if(panel.background !== true){
48664                 this.setActivePanel(panel);
48665             }
48666             this.fireEvent("paneladded", this, panel);
48667             return panel;
48668         }
48669         if(!this.tabs){
48670             this.initTabs();
48671         }else{
48672             this.initPanelAsTab(panel);
48673         }
48674         if(panel.background !== true){
48675             this.tabs.activate(panel.getEl().id);
48676         }
48677         this.fireEvent("paneladded", this, panel);
48678         return panel;
48679     },
48680
48681     /**
48682      * Hides the tab for the specified panel.
48683      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
48684      */
48685     hidePanel : function(panel){
48686         if(this.tabs && (panel = this.getPanel(panel))){
48687             this.tabs.hideTab(panel.getEl().id);
48688         }
48689     },
48690
48691     /**
48692      * Unhides the tab for a previously hidden panel.
48693      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
48694      */
48695     unhidePanel : function(panel){
48696         if(this.tabs && (panel = this.getPanel(panel))){
48697             this.tabs.unhideTab(panel.getEl().id);
48698         }
48699     },
48700
48701     clearPanels : function(){
48702         while(this.panels.getCount() > 0){
48703              this.remove(this.panels.first());
48704         }
48705     },
48706
48707     /**
48708      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
48709      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
48710      * @param {Boolean} preservePanel Overrides the config preservePanel option
48711      * @return {Roo.ContentPanel} The panel that was removed
48712      */
48713     remove : function(panel, preservePanel){
48714         panel = this.getPanel(panel);
48715         if(!panel){
48716             return null;
48717         }
48718         var e = {};
48719         this.fireEvent("beforeremove", this, panel, e);
48720         if(e.cancel === true){
48721             return null;
48722         }
48723         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
48724         var panelId = panel.getId();
48725         this.panels.removeKey(panelId);
48726         if(preservePanel){
48727             document.body.appendChild(panel.getEl().dom);
48728         }
48729         if(this.tabs){
48730             this.tabs.removeTab(panel.getEl().id);
48731         }else if (!preservePanel){
48732             this.bodyEl.dom.removeChild(panel.getEl().dom);
48733         }
48734         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
48735             var p = this.panels.first();
48736             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
48737             tempEl.appendChild(p.getEl().dom);
48738             this.bodyEl.update("");
48739             this.bodyEl.dom.appendChild(p.getEl().dom);
48740             tempEl = null;
48741             this.updateTitle(p.getTitle());
48742             this.tabs = null;
48743             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
48744             this.setActivePanel(p);
48745         }
48746         panel.setRegion(null);
48747         if(this.activePanel == panel){
48748             this.activePanel = null;
48749         }
48750         if(this.config.autoDestroy !== false && preservePanel !== true){
48751             try{panel.destroy();}catch(e){}
48752         }
48753         this.fireEvent("panelremoved", this, panel);
48754         return panel;
48755     },
48756
48757     /**
48758      * Returns the TabPanel component used by this region
48759      * @return {Roo.TabPanel}
48760      */
48761     getTabs : function(){
48762         return this.tabs;
48763     },
48764
48765     createTool : function(parentEl, className){
48766         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
48767             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
48768         btn.addClassOnOver("x-layout-tools-button-over");
48769         return btn;
48770     }
48771 });/*
48772  * Based on:
48773  * Ext JS Library 1.1.1
48774  * Copyright(c) 2006-2007, Ext JS, LLC.
48775  *
48776  * Originally Released Under LGPL - original licence link has changed is not relivant.
48777  *
48778  * Fork - LGPL
48779  * <script type="text/javascript">
48780  */
48781  
48782
48783
48784 /**
48785  * @class Roo.SplitLayoutRegion
48786  * @extends Roo.LayoutRegion
48787  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
48788  */
48789 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
48790     this.cursor = cursor;
48791     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
48792 };
48793
48794 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
48795     splitTip : "Drag to resize.",
48796     collapsibleSplitTip : "Drag to resize. Double click to hide.",
48797     useSplitTips : false,
48798
48799     applyConfig : function(config){
48800         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
48801         if(config.split){
48802             if(!this.split){
48803                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
48804                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
48805                 /** The SplitBar for this region 
48806                 * @type Roo.SplitBar */
48807                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
48808                 this.split.on("moved", this.onSplitMove, this);
48809                 this.split.useShim = config.useShim === true;
48810                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
48811                 if(this.useSplitTips){
48812                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
48813                 }
48814                 if(config.collapsible){
48815                     this.split.el.on("dblclick", this.collapse,  this);
48816                 }
48817             }
48818             if(typeof config.minSize != "undefined"){
48819                 this.split.minSize = config.minSize;
48820             }
48821             if(typeof config.maxSize != "undefined"){
48822                 this.split.maxSize = config.maxSize;
48823             }
48824             if(config.hideWhenEmpty || config.hidden || config.collapsed){
48825                 this.hideSplitter();
48826             }
48827         }
48828     },
48829
48830     getHMaxSize : function(){
48831          var cmax = this.config.maxSize || 10000;
48832          var center = this.mgr.getRegion("center");
48833          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
48834     },
48835
48836     getVMaxSize : function(){
48837          var cmax = this.config.maxSize || 10000;
48838          var center = this.mgr.getRegion("center");
48839          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
48840     },
48841
48842     onSplitMove : function(split, newSize){
48843         this.fireEvent("resized", this, newSize);
48844     },
48845     
48846     /** 
48847      * Returns the {@link Roo.SplitBar} for this region.
48848      * @return {Roo.SplitBar}
48849      */
48850     getSplitBar : function(){
48851         return this.split;
48852     },
48853     
48854     hide : function(){
48855         this.hideSplitter();
48856         Roo.SplitLayoutRegion.superclass.hide.call(this);
48857     },
48858
48859     hideSplitter : function(){
48860         if(this.split){
48861             this.split.el.setLocation(-2000,-2000);
48862             this.split.el.hide();
48863         }
48864     },
48865
48866     show : function(){
48867         if(this.split){
48868             this.split.el.show();
48869         }
48870         Roo.SplitLayoutRegion.superclass.show.call(this);
48871     },
48872     
48873     beforeSlide: function(){
48874         if(Roo.isGecko){// firefox overflow auto bug workaround
48875             this.bodyEl.clip();
48876             if(this.tabs) this.tabs.bodyEl.clip();
48877             if(this.activePanel){
48878                 this.activePanel.getEl().clip();
48879                 
48880                 if(this.activePanel.beforeSlide){
48881                     this.activePanel.beforeSlide();
48882                 }
48883             }
48884         }
48885     },
48886     
48887     afterSlide : function(){
48888         if(Roo.isGecko){// firefox overflow auto bug workaround
48889             this.bodyEl.unclip();
48890             if(this.tabs) this.tabs.bodyEl.unclip();
48891             if(this.activePanel){
48892                 this.activePanel.getEl().unclip();
48893                 if(this.activePanel.afterSlide){
48894                     this.activePanel.afterSlide();
48895                 }
48896             }
48897         }
48898     },
48899
48900     initAutoHide : function(){
48901         if(this.autoHide !== false){
48902             if(!this.autoHideHd){
48903                 var st = new Roo.util.DelayedTask(this.slideIn, this);
48904                 this.autoHideHd = {
48905                     "mouseout": function(e){
48906                         if(!e.within(this.el, true)){
48907                             st.delay(500);
48908                         }
48909                     },
48910                     "mouseover" : function(e){
48911                         st.cancel();
48912                     },
48913                     scope : this
48914                 };
48915             }
48916             this.el.on(this.autoHideHd);
48917         }
48918     },
48919
48920     clearAutoHide : function(){
48921         if(this.autoHide !== false){
48922             this.el.un("mouseout", this.autoHideHd.mouseout);
48923             this.el.un("mouseover", this.autoHideHd.mouseover);
48924         }
48925     },
48926
48927     clearMonitor : function(){
48928         Roo.get(document).un("click", this.slideInIf, this);
48929     },
48930
48931     // these names are backwards but not changed for compat
48932     slideOut : function(){
48933         if(this.isSlid || this.el.hasActiveFx()){
48934             return;
48935         }
48936         this.isSlid = true;
48937         if(this.collapseBtn){
48938             this.collapseBtn.hide();
48939         }
48940         this.closeBtnState = this.closeBtn.getStyle('display');
48941         this.closeBtn.hide();
48942         if(this.stickBtn){
48943             this.stickBtn.show();
48944         }
48945         this.el.show();
48946         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
48947         this.beforeSlide();
48948         this.el.setStyle("z-index", 10001);
48949         this.el.slideIn(this.getSlideAnchor(), {
48950             callback: function(){
48951                 this.afterSlide();
48952                 this.initAutoHide();
48953                 Roo.get(document).on("click", this.slideInIf, this);
48954                 this.fireEvent("slideshow", this);
48955             },
48956             scope: this,
48957             block: true
48958         });
48959     },
48960
48961     afterSlideIn : function(){
48962         this.clearAutoHide();
48963         this.isSlid = false;
48964         this.clearMonitor();
48965         this.el.setStyle("z-index", "");
48966         if(this.collapseBtn){
48967             this.collapseBtn.show();
48968         }
48969         this.closeBtn.setStyle('display', this.closeBtnState);
48970         if(this.stickBtn){
48971             this.stickBtn.hide();
48972         }
48973         this.fireEvent("slidehide", this);
48974     },
48975
48976     slideIn : function(cb){
48977         if(!this.isSlid || this.el.hasActiveFx()){
48978             Roo.callback(cb);
48979             return;
48980         }
48981         this.isSlid = false;
48982         this.beforeSlide();
48983         this.el.slideOut(this.getSlideAnchor(), {
48984             callback: function(){
48985                 this.el.setLeftTop(-10000, -10000);
48986                 this.afterSlide();
48987                 this.afterSlideIn();
48988                 Roo.callback(cb);
48989             },
48990             scope: this,
48991             block: true
48992         });
48993     },
48994     
48995     slideInIf : function(e){
48996         if(!e.within(this.el)){
48997             this.slideIn();
48998         }
48999     },
49000
49001     animateCollapse : function(){
49002         this.beforeSlide();
49003         this.el.setStyle("z-index", 20000);
49004         var anchor = this.getSlideAnchor();
49005         this.el.slideOut(anchor, {
49006             callback : function(){
49007                 this.el.setStyle("z-index", "");
49008                 this.collapsedEl.slideIn(anchor, {duration:.3});
49009                 this.afterSlide();
49010                 this.el.setLocation(-10000,-10000);
49011                 this.el.hide();
49012                 this.fireEvent("collapsed", this);
49013             },
49014             scope: this,
49015             block: true
49016         });
49017     },
49018
49019     animateExpand : function(){
49020         this.beforeSlide();
49021         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
49022         this.el.setStyle("z-index", 20000);
49023         this.collapsedEl.hide({
49024             duration:.1
49025         });
49026         this.el.slideIn(this.getSlideAnchor(), {
49027             callback : function(){
49028                 this.el.setStyle("z-index", "");
49029                 this.afterSlide();
49030                 if(this.split){
49031                     this.split.el.show();
49032                 }
49033                 this.fireEvent("invalidated", this);
49034                 this.fireEvent("expanded", this);
49035             },
49036             scope: this,
49037             block: true
49038         });
49039     },
49040
49041     anchors : {
49042         "west" : "left",
49043         "east" : "right",
49044         "north" : "top",
49045         "south" : "bottom"
49046     },
49047
49048     sanchors : {
49049         "west" : "l",
49050         "east" : "r",
49051         "north" : "t",
49052         "south" : "b"
49053     },
49054
49055     canchors : {
49056         "west" : "tl-tr",
49057         "east" : "tr-tl",
49058         "north" : "tl-bl",
49059         "south" : "bl-tl"
49060     },
49061
49062     getAnchor : function(){
49063         return this.anchors[this.position];
49064     },
49065
49066     getCollapseAnchor : function(){
49067         return this.canchors[this.position];
49068     },
49069
49070     getSlideAnchor : function(){
49071         return this.sanchors[this.position];
49072     },
49073
49074     getAlignAdj : function(){
49075         var cm = this.cmargins;
49076         switch(this.position){
49077             case "west":
49078                 return [0, 0];
49079             break;
49080             case "east":
49081                 return [0, 0];
49082             break;
49083             case "north":
49084                 return [0, 0];
49085             break;
49086             case "south":
49087                 return [0, 0];
49088             break;
49089         }
49090     },
49091
49092     getExpandAdj : function(){
49093         var c = this.collapsedEl, cm = this.cmargins;
49094         switch(this.position){
49095             case "west":
49096                 return [-(cm.right+c.getWidth()+cm.left), 0];
49097             break;
49098             case "east":
49099                 return [cm.right+c.getWidth()+cm.left, 0];
49100             break;
49101             case "north":
49102                 return [0, -(cm.top+cm.bottom+c.getHeight())];
49103             break;
49104             case "south":
49105                 return [0, cm.top+cm.bottom+c.getHeight()];
49106             break;
49107         }
49108     }
49109 });/*
49110  * Based on:
49111  * Ext JS Library 1.1.1
49112  * Copyright(c) 2006-2007, Ext JS, LLC.
49113  *
49114  * Originally Released Under LGPL - original licence link has changed is not relivant.
49115  *
49116  * Fork - LGPL
49117  * <script type="text/javascript">
49118  */
49119 /*
49120  * These classes are private internal classes
49121  */
49122 Roo.CenterLayoutRegion = function(mgr, config){
49123     Roo.LayoutRegion.call(this, mgr, config, "center");
49124     this.visible = true;
49125     this.minWidth = config.minWidth || 20;
49126     this.minHeight = config.minHeight || 20;
49127 };
49128
49129 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
49130     hide : function(){
49131         // center panel can't be hidden
49132     },
49133     
49134     show : function(){
49135         // center panel can't be hidden
49136     },
49137     
49138     getMinWidth: function(){
49139         return this.minWidth;
49140     },
49141     
49142     getMinHeight: function(){
49143         return this.minHeight;
49144     }
49145 });
49146
49147
49148 Roo.NorthLayoutRegion = function(mgr, config){
49149     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
49150     if(this.split){
49151         this.split.placement = Roo.SplitBar.TOP;
49152         this.split.orientation = Roo.SplitBar.VERTICAL;
49153         this.split.el.addClass("x-layout-split-v");
49154     }
49155     var size = config.initialSize || config.height;
49156     if(typeof size != "undefined"){
49157         this.el.setHeight(size);
49158     }
49159 };
49160 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
49161     orientation: Roo.SplitBar.VERTICAL,
49162     getBox : function(){
49163         if(this.collapsed){
49164             return this.collapsedEl.getBox();
49165         }
49166         var box = this.el.getBox();
49167         if(this.split){
49168             box.height += this.split.el.getHeight();
49169         }
49170         return box;
49171     },
49172     
49173     updateBox : function(box){
49174         if(this.split && !this.collapsed){
49175             box.height -= this.split.el.getHeight();
49176             this.split.el.setLeft(box.x);
49177             this.split.el.setTop(box.y+box.height);
49178             this.split.el.setWidth(box.width);
49179         }
49180         if(this.collapsed){
49181             this.updateBody(box.width, null);
49182         }
49183         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49184     }
49185 });
49186
49187 Roo.SouthLayoutRegion = function(mgr, config){
49188     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
49189     if(this.split){
49190         this.split.placement = Roo.SplitBar.BOTTOM;
49191         this.split.orientation = Roo.SplitBar.VERTICAL;
49192         this.split.el.addClass("x-layout-split-v");
49193     }
49194     var size = config.initialSize || config.height;
49195     if(typeof size != "undefined"){
49196         this.el.setHeight(size);
49197     }
49198 };
49199 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
49200     orientation: Roo.SplitBar.VERTICAL,
49201     getBox : function(){
49202         if(this.collapsed){
49203             return this.collapsedEl.getBox();
49204         }
49205         var box = this.el.getBox();
49206         if(this.split){
49207             var sh = this.split.el.getHeight();
49208             box.height += sh;
49209             box.y -= sh;
49210         }
49211         return box;
49212     },
49213     
49214     updateBox : function(box){
49215         if(this.split && !this.collapsed){
49216             var sh = this.split.el.getHeight();
49217             box.height -= sh;
49218             box.y += sh;
49219             this.split.el.setLeft(box.x);
49220             this.split.el.setTop(box.y-sh);
49221             this.split.el.setWidth(box.width);
49222         }
49223         if(this.collapsed){
49224             this.updateBody(box.width, null);
49225         }
49226         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49227     }
49228 });
49229
49230 Roo.EastLayoutRegion = function(mgr, config){
49231     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
49232     if(this.split){
49233         this.split.placement = Roo.SplitBar.RIGHT;
49234         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49235         this.split.el.addClass("x-layout-split-h");
49236     }
49237     var size = config.initialSize || config.width;
49238     if(typeof size != "undefined"){
49239         this.el.setWidth(size);
49240     }
49241 };
49242 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
49243     orientation: Roo.SplitBar.HORIZONTAL,
49244     getBox : function(){
49245         if(this.collapsed){
49246             return this.collapsedEl.getBox();
49247         }
49248         var box = this.el.getBox();
49249         if(this.split){
49250             var sw = this.split.el.getWidth();
49251             box.width += sw;
49252             box.x -= sw;
49253         }
49254         return box;
49255     },
49256
49257     updateBox : function(box){
49258         if(this.split && !this.collapsed){
49259             var sw = this.split.el.getWidth();
49260             box.width -= sw;
49261             this.split.el.setLeft(box.x);
49262             this.split.el.setTop(box.y);
49263             this.split.el.setHeight(box.height);
49264             box.x += sw;
49265         }
49266         if(this.collapsed){
49267             this.updateBody(null, box.height);
49268         }
49269         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49270     }
49271 });
49272
49273 Roo.WestLayoutRegion = function(mgr, config){
49274     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
49275     if(this.split){
49276         this.split.placement = Roo.SplitBar.LEFT;
49277         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49278         this.split.el.addClass("x-layout-split-h");
49279     }
49280     var size = config.initialSize || config.width;
49281     if(typeof size != "undefined"){
49282         this.el.setWidth(size);
49283     }
49284 };
49285 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
49286     orientation: Roo.SplitBar.HORIZONTAL,
49287     getBox : function(){
49288         if(this.collapsed){
49289             return this.collapsedEl.getBox();
49290         }
49291         var box = this.el.getBox();
49292         if(this.split){
49293             box.width += this.split.el.getWidth();
49294         }
49295         return box;
49296     },
49297     
49298     updateBox : function(box){
49299         if(this.split && !this.collapsed){
49300             var sw = this.split.el.getWidth();
49301             box.width -= sw;
49302             this.split.el.setLeft(box.x+box.width);
49303             this.split.el.setTop(box.y);
49304             this.split.el.setHeight(box.height);
49305         }
49306         if(this.collapsed){
49307             this.updateBody(null, box.height);
49308         }
49309         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49310     }
49311 });
49312 /*
49313  * Based on:
49314  * Ext JS Library 1.1.1
49315  * Copyright(c) 2006-2007, Ext JS, LLC.
49316  *
49317  * Originally Released Under LGPL - original licence link has changed is not relivant.
49318  *
49319  * Fork - LGPL
49320  * <script type="text/javascript">
49321  */
49322  
49323  
49324 /*
49325  * Private internal class for reading and applying state
49326  */
49327 Roo.LayoutStateManager = function(layout){
49328      // default empty state
49329      this.state = {
49330         north: {},
49331         south: {},
49332         east: {},
49333         west: {}       
49334     };
49335 };
49336
49337 Roo.LayoutStateManager.prototype = {
49338     init : function(layout, provider){
49339         this.provider = provider;
49340         var state = provider.get(layout.id+"-layout-state");
49341         if(state){
49342             var wasUpdating = layout.isUpdating();
49343             if(!wasUpdating){
49344                 layout.beginUpdate();
49345             }
49346             for(var key in state){
49347                 if(typeof state[key] != "function"){
49348                     var rstate = state[key];
49349                     var r = layout.getRegion(key);
49350                     if(r && rstate){
49351                         if(rstate.size){
49352                             r.resizeTo(rstate.size);
49353                         }
49354                         if(rstate.collapsed == true){
49355                             r.collapse(true);
49356                         }else{
49357                             r.expand(null, true);
49358                         }
49359                     }
49360                 }
49361             }
49362             if(!wasUpdating){
49363                 layout.endUpdate();
49364             }
49365             this.state = state; 
49366         }
49367         this.layout = layout;
49368         layout.on("regionresized", this.onRegionResized, this);
49369         layout.on("regioncollapsed", this.onRegionCollapsed, this);
49370         layout.on("regionexpanded", this.onRegionExpanded, this);
49371     },
49372     
49373     storeState : function(){
49374         this.provider.set(this.layout.id+"-layout-state", this.state);
49375     },
49376     
49377     onRegionResized : function(region, newSize){
49378         this.state[region.getPosition()].size = newSize;
49379         this.storeState();
49380     },
49381     
49382     onRegionCollapsed : function(region){
49383         this.state[region.getPosition()].collapsed = true;
49384         this.storeState();
49385     },
49386     
49387     onRegionExpanded : function(region){
49388         this.state[region.getPosition()].collapsed = false;
49389         this.storeState();
49390     }
49391 };/*
49392  * Based on:
49393  * Ext JS Library 1.1.1
49394  * Copyright(c) 2006-2007, Ext JS, LLC.
49395  *
49396  * Originally Released Under LGPL - original licence link has changed is not relivant.
49397  *
49398  * Fork - LGPL
49399  * <script type="text/javascript">
49400  */
49401 /**
49402  * @class Roo.ContentPanel
49403  * @extends Roo.util.Observable
49404  * A basic ContentPanel element.
49405  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
49406  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
49407  * @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
49408  * @cfg {Boolean}   closable      True if the panel can be closed/removed
49409  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
49410  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
49411  * @cfg {Toolbar}   toolbar       A toolbar for this panel
49412  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
49413  * @cfg {String} title          The title for this panel
49414  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
49415  * @cfg {String} url            Calls {@link #setUrl} with this value
49416  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
49417  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
49418  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
49419  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
49420
49421  * @constructor
49422  * Create a new ContentPanel.
49423  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
49424  * @param {String/Object} config A string to set only the title or a config object
49425  * @param {String} content (optional) Set the HTML content for this panel
49426  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
49427  */
49428 Roo.ContentPanel = function(el, config, content){
49429     
49430      
49431     /*
49432     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
49433         config = el;
49434         el = Roo.id();
49435     }
49436     if (config && config.parentLayout) { 
49437         el = config.parentLayout.el.createChild(); 
49438     }
49439     */
49440     if(el.autoCreate){ // xtype is available if this is called from factory
49441         config = el;
49442         el = Roo.id();
49443     }
49444     this.el = Roo.get(el);
49445     if(!this.el && config && config.autoCreate){
49446         if(typeof config.autoCreate == "object"){
49447             if(!config.autoCreate.id){
49448                 config.autoCreate.id = config.id||el;
49449             }
49450             this.el = Roo.DomHelper.append(document.body,
49451                         config.autoCreate, true);
49452         }else{
49453             this.el = Roo.DomHelper.append(document.body,
49454                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
49455         }
49456     }
49457     this.closable = false;
49458     this.loaded = false;
49459     this.active = false;
49460     if(typeof config == "string"){
49461         this.title = config;
49462     }else{
49463         Roo.apply(this, config);
49464     }
49465     
49466     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
49467         this.wrapEl = this.el.wrap();
49468         this.toolbar.container = this.el.insertSibling(false, 'before');
49469         this.toolbar = new Roo.Toolbar(this.toolbar);
49470     }
49471     
49472     // xtype created footer. - not sure if will work as we normally have to render first..
49473     if (this.footer && !this.footer.el && this.footer.xtype) {
49474         if (!this.wrapEl) {
49475             this.wrapEl = this.el.wrap();
49476         }
49477     
49478         this.footer.container = this.wrapEl.createChild();
49479          
49480         this.footer = Roo.factory(this.footer, Roo);
49481         
49482     }
49483     
49484     if(this.resizeEl){
49485         this.resizeEl = Roo.get(this.resizeEl, true);
49486     }else{
49487         this.resizeEl = this.el;
49488     }
49489     // handle view.xtype
49490     
49491  
49492     
49493     
49494     this.addEvents({
49495         /**
49496          * @event activate
49497          * Fires when this panel is activated. 
49498          * @param {Roo.ContentPanel} this
49499          */
49500         "activate" : true,
49501         /**
49502          * @event deactivate
49503          * Fires when this panel is activated. 
49504          * @param {Roo.ContentPanel} this
49505          */
49506         "deactivate" : true,
49507
49508         /**
49509          * @event resize
49510          * Fires when this panel is resized if fitToFrame is true.
49511          * @param {Roo.ContentPanel} this
49512          * @param {Number} width The width after any component adjustments
49513          * @param {Number} height The height after any component adjustments
49514          */
49515         "resize" : true,
49516         
49517          /**
49518          * @event render
49519          * Fires when this tab is created
49520          * @param {Roo.ContentPanel} this
49521          */
49522         "render" : true
49523         
49524         
49525         
49526     });
49527     
49528
49529     
49530     
49531     if(this.autoScroll){
49532         this.resizeEl.setStyle("overflow", "auto");
49533     } else {
49534         // fix randome scrolling
49535         this.el.on('scroll', function() {
49536             Roo.log('fix random scolling');
49537             this.scrollTo('top',0); 
49538         });
49539     }
49540     content = content || this.content;
49541     if(content){
49542         this.setContent(content);
49543     }
49544     if(config && config.url){
49545         this.setUrl(this.url, this.params, this.loadOnce);
49546     }
49547     
49548     
49549     
49550     Roo.ContentPanel.superclass.constructor.call(this);
49551     
49552     if (this.view && typeof(this.view.xtype) != 'undefined') {
49553         this.view.el = this.el.appendChild(document.createElement("div"));
49554         this.view = Roo.factory(this.view); 
49555         this.view.render  &&  this.view.render(false, '');  
49556     }
49557     
49558     
49559     this.fireEvent('render', this);
49560 };
49561
49562 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
49563     tabTip:'',
49564     setRegion : function(region){
49565         this.region = region;
49566         if(region){
49567            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
49568         }else{
49569            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
49570         } 
49571     },
49572     
49573     /**
49574      * Returns the toolbar for this Panel if one was configured. 
49575      * @return {Roo.Toolbar} 
49576      */
49577     getToolbar : function(){
49578         return this.toolbar;
49579     },
49580     
49581     setActiveState : function(active){
49582         this.active = active;
49583         if(!active){
49584             this.fireEvent("deactivate", this);
49585         }else{
49586             this.fireEvent("activate", this);
49587         }
49588     },
49589     /**
49590      * Updates this panel's element
49591      * @param {String} content The new content
49592      * @param {Boolean} loadScripts (optional) true to look for and process scripts
49593     */
49594     setContent : function(content, loadScripts){
49595         this.el.update(content, loadScripts);
49596     },
49597
49598     ignoreResize : function(w, h){
49599         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
49600             return true;
49601         }else{
49602             this.lastSize = {width: w, height: h};
49603             return false;
49604         }
49605     },
49606     /**
49607      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
49608      * @return {Roo.UpdateManager} The UpdateManager
49609      */
49610     getUpdateManager : function(){
49611         return this.el.getUpdateManager();
49612     },
49613      /**
49614      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
49615      * @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:
49616 <pre><code>
49617 panel.load({
49618     url: "your-url.php",
49619     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
49620     callback: yourFunction,
49621     scope: yourObject, //(optional scope)
49622     discardUrl: false,
49623     nocache: false,
49624     text: "Loading...",
49625     timeout: 30,
49626     scripts: false
49627 });
49628 </code></pre>
49629      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
49630      * 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.
49631      * @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}
49632      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
49633      * @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.
49634      * @return {Roo.ContentPanel} this
49635      */
49636     load : function(){
49637         var um = this.el.getUpdateManager();
49638         um.update.apply(um, arguments);
49639         return this;
49640     },
49641
49642
49643     /**
49644      * 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.
49645      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
49646      * @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)
49647      * @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)
49648      * @return {Roo.UpdateManager} The UpdateManager
49649      */
49650     setUrl : function(url, params, loadOnce){
49651         if(this.refreshDelegate){
49652             this.removeListener("activate", this.refreshDelegate);
49653         }
49654         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
49655         this.on("activate", this.refreshDelegate);
49656         return this.el.getUpdateManager();
49657     },
49658     
49659     _handleRefresh : function(url, params, loadOnce){
49660         if(!loadOnce || !this.loaded){
49661             var updater = this.el.getUpdateManager();
49662             updater.update(url, params, this._setLoaded.createDelegate(this));
49663         }
49664     },
49665     
49666     _setLoaded : function(){
49667         this.loaded = true;
49668     }, 
49669     
49670     /**
49671      * Returns this panel's id
49672      * @return {String} 
49673      */
49674     getId : function(){
49675         return this.el.id;
49676     },
49677     
49678     /** 
49679      * Returns this panel's element - used by regiosn to add.
49680      * @return {Roo.Element} 
49681      */
49682     getEl : function(){
49683         return this.wrapEl || this.el;
49684     },
49685     
49686     adjustForComponents : function(width, height)
49687     {
49688         //Roo.log('adjustForComponents ');
49689         if(this.resizeEl != this.el){
49690             width -= this.el.getFrameWidth('lr');
49691             height -= this.el.getFrameWidth('tb');
49692         }
49693         if(this.toolbar){
49694             var te = this.toolbar.getEl();
49695             height -= te.getHeight();
49696             te.setWidth(width);
49697         }
49698         if(this.footer){
49699             var te = this.footer.getEl();
49700             Roo.log("footer:" + te.getHeight());
49701             
49702             height -= te.getHeight();
49703             te.setWidth(width);
49704         }
49705         
49706         
49707         if(this.adjustments){
49708             width += this.adjustments[0];
49709             height += this.adjustments[1];
49710         }
49711         return {"width": width, "height": height};
49712     },
49713     
49714     setSize : function(width, height){
49715         if(this.fitToFrame && !this.ignoreResize(width, height)){
49716             if(this.fitContainer && this.resizeEl != this.el){
49717                 this.el.setSize(width, height);
49718             }
49719             var size = this.adjustForComponents(width, height);
49720             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
49721             this.fireEvent('resize', this, size.width, size.height);
49722         }
49723     },
49724     
49725     /**
49726      * Returns this panel's title
49727      * @return {String} 
49728      */
49729     getTitle : function(){
49730         return this.title;
49731     },
49732     
49733     /**
49734      * Set this panel's title
49735      * @param {String} title
49736      */
49737     setTitle : function(title){
49738         this.title = title;
49739         if(this.region){
49740             this.region.updatePanelTitle(this, title);
49741         }
49742     },
49743     
49744     /**
49745      * Returns true is this panel was configured to be closable
49746      * @return {Boolean} 
49747      */
49748     isClosable : function(){
49749         return this.closable;
49750     },
49751     
49752     beforeSlide : function(){
49753         this.el.clip();
49754         this.resizeEl.clip();
49755     },
49756     
49757     afterSlide : function(){
49758         this.el.unclip();
49759         this.resizeEl.unclip();
49760     },
49761     
49762     /**
49763      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
49764      *   Will fail silently if the {@link #setUrl} method has not been called.
49765      *   This does not activate the panel, just updates its content.
49766      */
49767     refresh : function(){
49768         if(this.refreshDelegate){
49769            this.loaded = false;
49770            this.refreshDelegate();
49771         }
49772     },
49773     
49774     /**
49775      * Destroys this panel
49776      */
49777     destroy : function(){
49778         this.el.removeAllListeners();
49779         var tempEl = document.createElement("span");
49780         tempEl.appendChild(this.el.dom);
49781         tempEl.innerHTML = "";
49782         this.el.remove();
49783         this.el = null;
49784     },
49785     
49786     /**
49787      * form - if the content panel contains a form - this is a reference to it.
49788      * @type {Roo.form.Form}
49789      */
49790     form : false,
49791     /**
49792      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
49793      *    This contains a reference to it.
49794      * @type {Roo.View}
49795      */
49796     view : false,
49797     
49798       /**
49799      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
49800      * <pre><code>
49801
49802 layout.addxtype({
49803        xtype : 'Form',
49804        items: [ .... ]
49805    }
49806 );
49807
49808 </code></pre>
49809      * @param {Object} cfg Xtype definition of item to add.
49810      */
49811     
49812     addxtype : function(cfg) {
49813         // add form..
49814         if (cfg.xtype.match(/^Form$/)) {
49815             
49816             var el;
49817             //if (this.footer) {
49818             //    el = this.footer.container.insertSibling(false, 'before');
49819             //} else {
49820                 el = this.el.createChild();
49821             //}
49822
49823             this.form = new  Roo.form.Form(cfg);
49824             
49825             
49826             if ( this.form.allItems.length) this.form.render(el.dom);
49827             return this.form;
49828         }
49829         // should only have one of theses..
49830         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
49831             // views.. should not be just added - used named prop 'view''
49832             
49833             cfg.el = this.el.appendChild(document.createElement("div"));
49834             // factory?
49835             
49836             var ret = new Roo.factory(cfg);
49837              
49838              ret.render && ret.render(false, ''); // render blank..
49839             this.view = ret;
49840             return ret;
49841         }
49842         return false;
49843     }
49844 });
49845
49846 /**
49847  * @class Roo.GridPanel
49848  * @extends Roo.ContentPanel
49849  * @constructor
49850  * Create a new GridPanel.
49851  * @param {Roo.grid.Grid} grid The grid for this panel
49852  * @param {String/Object} config A string to set only the panel's title, or a config object
49853  */
49854 Roo.GridPanel = function(grid, config){
49855     
49856   
49857     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
49858         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
49859         
49860     this.wrapper.dom.appendChild(grid.getGridEl().dom);
49861     
49862     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
49863     
49864     if(this.toolbar){
49865         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
49866     }
49867     // xtype created footer. - not sure if will work as we normally have to render first..
49868     if (this.footer && !this.footer.el && this.footer.xtype) {
49869         
49870         this.footer.container = this.grid.getView().getFooterPanel(true);
49871         this.footer.dataSource = this.grid.dataSource;
49872         this.footer = Roo.factory(this.footer, Roo);
49873         
49874     }
49875     
49876     grid.monitorWindowResize = false; // turn off autosizing
49877     grid.autoHeight = false;
49878     grid.autoWidth = false;
49879     this.grid = grid;
49880     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
49881 };
49882
49883 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
49884     getId : function(){
49885         return this.grid.id;
49886     },
49887     
49888     /**
49889      * Returns the grid for this panel
49890      * @return {Roo.grid.Grid} 
49891      */
49892     getGrid : function(){
49893         return this.grid;    
49894     },
49895     
49896     setSize : function(width, height){
49897         if(!this.ignoreResize(width, height)){
49898             var grid = this.grid;
49899             var size = this.adjustForComponents(width, height);
49900             grid.getGridEl().setSize(size.width, size.height);
49901             grid.autoSize();
49902         }
49903     },
49904     
49905     beforeSlide : function(){
49906         this.grid.getView().scroller.clip();
49907     },
49908     
49909     afterSlide : function(){
49910         this.grid.getView().scroller.unclip();
49911     },
49912     
49913     destroy : function(){
49914         this.grid.destroy();
49915         delete this.grid;
49916         Roo.GridPanel.superclass.destroy.call(this); 
49917     }
49918 });
49919
49920
49921 /**
49922  * @class Roo.NestedLayoutPanel
49923  * @extends Roo.ContentPanel
49924  * @constructor
49925  * Create a new NestedLayoutPanel.
49926  * 
49927  * 
49928  * @param {Roo.BorderLayout} layout The layout for this panel
49929  * @param {String/Object} config A string to set only the title or a config object
49930  */
49931 Roo.NestedLayoutPanel = function(layout, config)
49932 {
49933     // construct with only one argument..
49934     /* FIXME - implement nicer consturctors
49935     if (layout.layout) {
49936         config = layout;
49937         layout = config.layout;
49938         delete config.layout;
49939     }
49940     if (layout.xtype && !layout.getEl) {
49941         // then layout needs constructing..
49942         layout = Roo.factory(layout, Roo);
49943     }
49944     */
49945     
49946     
49947     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
49948     
49949     layout.monitorWindowResize = false; // turn off autosizing
49950     this.layout = layout;
49951     this.layout.getEl().addClass("x-layout-nested-layout");
49952     
49953     
49954     
49955     
49956 };
49957
49958 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
49959
49960     setSize : function(width, height){
49961         if(!this.ignoreResize(width, height)){
49962             var size = this.adjustForComponents(width, height);
49963             var el = this.layout.getEl();
49964             el.setSize(size.width, size.height);
49965             var touch = el.dom.offsetWidth;
49966             this.layout.layout();
49967             // ie requires a double layout on the first pass
49968             if(Roo.isIE && !this.initialized){
49969                 this.initialized = true;
49970                 this.layout.layout();
49971             }
49972         }
49973     },
49974     
49975     // activate all subpanels if not currently active..
49976     
49977     setActiveState : function(active){
49978         this.active = active;
49979         if(!active){
49980             this.fireEvent("deactivate", this);
49981             return;
49982         }
49983         
49984         this.fireEvent("activate", this);
49985         // not sure if this should happen before or after..
49986         if (!this.layout) {
49987             return; // should not happen..
49988         }
49989         var reg = false;
49990         for (var r in this.layout.regions) {
49991             reg = this.layout.getRegion(r);
49992             if (reg.getActivePanel()) {
49993                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
49994                 reg.setActivePanel(reg.getActivePanel());
49995                 continue;
49996             }
49997             if (!reg.panels.length) {
49998                 continue;
49999             }
50000             reg.showPanel(reg.getPanel(0));
50001         }
50002         
50003         
50004         
50005         
50006     },
50007     
50008     /**
50009      * Returns the nested BorderLayout for this panel
50010      * @return {Roo.BorderLayout} 
50011      */
50012     getLayout : function(){
50013         return this.layout;
50014     },
50015     
50016      /**
50017      * Adds a xtype elements to the layout of the nested panel
50018      * <pre><code>
50019
50020 panel.addxtype({
50021        xtype : 'ContentPanel',
50022        region: 'west',
50023        items: [ .... ]
50024    }
50025 );
50026
50027 panel.addxtype({
50028         xtype : 'NestedLayoutPanel',
50029         region: 'west',
50030         layout: {
50031            center: { },
50032            west: { }   
50033         },
50034         items : [ ... list of content panels or nested layout panels.. ]
50035    }
50036 );
50037 </code></pre>
50038      * @param {Object} cfg Xtype definition of item to add.
50039      */
50040     addxtype : function(cfg) {
50041         return this.layout.addxtype(cfg);
50042     
50043     }
50044 });
50045
50046 Roo.ScrollPanel = function(el, config, content){
50047     config = config || {};
50048     config.fitToFrame = true;
50049     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
50050     
50051     this.el.dom.style.overflow = "hidden";
50052     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
50053     this.el.removeClass("x-layout-inactive-content");
50054     this.el.on("mousewheel", this.onWheel, this);
50055
50056     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
50057     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
50058     up.unselectable(); down.unselectable();
50059     up.on("click", this.scrollUp, this);
50060     down.on("click", this.scrollDown, this);
50061     up.addClassOnOver("x-scroller-btn-over");
50062     down.addClassOnOver("x-scroller-btn-over");
50063     up.addClassOnClick("x-scroller-btn-click");
50064     down.addClassOnClick("x-scroller-btn-click");
50065     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
50066
50067     this.resizeEl = this.el;
50068     this.el = wrap; this.up = up; this.down = down;
50069 };
50070
50071 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
50072     increment : 100,
50073     wheelIncrement : 5,
50074     scrollUp : function(){
50075         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
50076     },
50077
50078     scrollDown : function(){
50079         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
50080     },
50081
50082     afterScroll : function(){
50083         var el = this.resizeEl;
50084         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
50085         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50086         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50087     },
50088
50089     setSize : function(){
50090         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
50091         this.afterScroll();
50092     },
50093
50094     onWheel : function(e){
50095         var d = e.getWheelDelta();
50096         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
50097         this.afterScroll();
50098         e.stopEvent();
50099     },
50100
50101     setContent : function(content, loadScripts){
50102         this.resizeEl.update(content, loadScripts);
50103     }
50104
50105 });
50106
50107
50108
50109
50110
50111
50112
50113
50114
50115 /**
50116  * @class Roo.TreePanel
50117  * @extends Roo.ContentPanel
50118  * @constructor
50119  * Create a new TreePanel. - defaults to fit/scoll contents.
50120  * @param {String/Object} config A string to set only the panel's title, or a config object
50121  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
50122  */
50123 Roo.TreePanel = function(config){
50124     var el = config.el;
50125     var tree = config.tree;
50126     delete config.tree; 
50127     delete config.el; // hopefull!
50128     
50129     // wrapper for IE7 strict & safari scroll issue
50130     
50131     var treeEl = el.createChild();
50132     config.resizeEl = treeEl;
50133     
50134     
50135     
50136     Roo.TreePanel.superclass.constructor.call(this, el, config);
50137  
50138  
50139     this.tree = new Roo.tree.TreePanel(treeEl , tree);
50140     //console.log(tree);
50141     this.on('activate', function()
50142     {
50143         if (this.tree.rendered) {
50144             return;
50145         }
50146         //console.log('render tree');
50147         this.tree.render();
50148     });
50149     // this should not be needed.. - it's actually the 'el' that resizes?
50150     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
50151     
50152     //this.on('resize',  function (cp, w, h) {
50153     //        this.tree.innerCt.setWidth(w);
50154     //        this.tree.innerCt.setHeight(h);
50155     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
50156     //});
50157
50158         
50159     
50160 };
50161
50162 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
50163     fitToFrame : true,
50164     autoScroll : true
50165 });
50166
50167
50168
50169
50170
50171
50172
50173
50174
50175
50176
50177 /*
50178  * Based on:
50179  * Ext JS Library 1.1.1
50180  * Copyright(c) 2006-2007, Ext JS, LLC.
50181  *
50182  * Originally Released Under LGPL - original licence link has changed is not relivant.
50183  *
50184  * Fork - LGPL
50185  * <script type="text/javascript">
50186  */
50187  
50188
50189 /**
50190  * @class Roo.ReaderLayout
50191  * @extends Roo.BorderLayout
50192  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
50193  * center region containing two nested regions (a top one for a list view and one for item preview below),
50194  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
50195  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
50196  * expedites the setup of the overall layout and regions for this common application style.
50197  * Example:
50198  <pre><code>
50199 var reader = new Roo.ReaderLayout();
50200 var CP = Roo.ContentPanel;  // shortcut for adding
50201
50202 reader.beginUpdate();
50203 reader.add("north", new CP("north", "North"));
50204 reader.add("west", new CP("west", {title: "West"}));
50205 reader.add("east", new CP("east", {title: "East"}));
50206
50207 reader.regions.listView.add(new CP("listView", "List"));
50208 reader.regions.preview.add(new CP("preview", "Preview"));
50209 reader.endUpdate();
50210 </code></pre>
50211 * @constructor
50212 * Create a new ReaderLayout
50213 * @param {Object} config Configuration options
50214 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
50215 * document.body if omitted)
50216 */
50217 Roo.ReaderLayout = function(config, renderTo){
50218     var c = config || {size:{}};
50219     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
50220         north: c.north !== false ? Roo.apply({
50221             split:false,
50222             initialSize: 32,
50223             titlebar: false
50224         }, c.north) : false,
50225         west: c.west !== false ? Roo.apply({
50226             split:true,
50227             initialSize: 200,
50228             minSize: 175,
50229             maxSize: 400,
50230             titlebar: true,
50231             collapsible: true,
50232             animate: true,
50233             margins:{left:5,right:0,bottom:5,top:5},
50234             cmargins:{left:5,right:5,bottom:5,top:5}
50235         }, c.west) : false,
50236         east: c.east !== false ? Roo.apply({
50237             split:true,
50238             initialSize: 200,
50239             minSize: 175,
50240             maxSize: 400,
50241             titlebar: true,
50242             collapsible: true,
50243             animate: true,
50244             margins:{left:0,right:5,bottom:5,top:5},
50245             cmargins:{left:5,right:5,bottom:5,top:5}
50246         }, c.east) : false,
50247         center: Roo.apply({
50248             tabPosition: 'top',
50249             autoScroll:false,
50250             closeOnTab: true,
50251             titlebar:false,
50252             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
50253         }, c.center)
50254     });
50255
50256     this.el.addClass('x-reader');
50257
50258     this.beginUpdate();
50259
50260     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
50261         south: c.preview !== false ? Roo.apply({
50262             split:true,
50263             initialSize: 200,
50264             minSize: 100,
50265             autoScroll:true,
50266             collapsible:true,
50267             titlebar: true,
50268             cmargins:{top:5,left:0, right:0, bottom:0}
50269         }, c.preview) : false,
50270         center: Roo.apply({
50271             autoScroll:false,
50272             titlebar:false,
50273             minHeight:200
50274         }, c.listView)
50275     });
50276     this.add('center', new Roo.NestedLayoutPanel(inner,
50277             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
50278
50279     this.endUpdate();
50280
50281     this.regions.preview = inner.getRegion('south');
50282     this.regions.listView = inner.getRegion('center');
50283 };
50284
50285 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
50286  * Based on:
50287  * Ext JS Library 1.1.1
50288  * Copyright(c) 2006-2007, Ext JS, LLC.
50289  *
50290  * Originally Released Under LGPL - original licence link has changed is not relivant.
50291  *
50292  * Fork - LGPL
50293  * <script type="text/javascript">
50294  */
50295  
50296 /**
50297  * @class Roo.grid.Grid
50298  * @extends Roo.util.Observable
50299  * This class represents the primary interface of a component based grid control.
50300  * <br><br>Usage:<pre><code>
50301  var grid = new Roo.grid.Grid("my-container-id", {
50302      ds: myDataStore,
50303      cm: myColModel,
50304      selModel: mySelectionModel,
50305      autoSizeColumns: true,
50306      monitorWindowResize: false,
50307      trackMouseOver: true
50308  });
50309  // set any options
50310  grid.render();
50311  * </code></pre>
50312  * <b>Common Problems:</b><br/>
50313  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
50314  * element will correct this<br/>
50315  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
50316  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
50317  * are unpredictable.<br/>
50318  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
50319  * grid to calculate dimensions/offsets.<br/>
50320   * @constructor
50321  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50322  * The container MUST have some type of size defined for the grid to fill. The container will be
50323  * automatically set to position relative if it isn't already.
50324  * @param {Object} config A config object that sets properties on this grid.
50325  */
50326 Roo.grid.Grid = function(container, config){
50327         // initialize the container
50328         this.container = Roo.get(container);
50329         this.container.update("");
50330         this.container.setStyle("overflow", "hidden");
50331     this.container.addClass('x-grid-container');
50332
50333     this.id = this.container.id;
50334
50335     Roo.apply(this, config);
50336     // check and correct shorthanded configs
50337     if(this.ds){
50338         this.dataSource = this.ds;
50339         delete this.ds;
50340     }
50341     if(this.cm){
50342         this.colModel = this.cm;
50343         delete this.cm;
50344     }
50345     if(this.sm){
50346         this.selModel = this.sm;
50347         delete this.sm;
50348     }
50349
50350     if (this.selModel) {
50351         this.selModel = Roo.factory(this.selModel, Roo.grid);
50352         this.sm = this.selModel;
50353         this.sm.xmodule = this.xmodule || false;
50354     }
50355     if (typeof(this.colModel.config) == 'undefined') {
50356         this.colModel = new Roo.grid.ColumnModel(this.colModel);
50357         this.cm = this.colModel;
50358         this.cm.xmodule = this.xmodule || false;
50359     }
50360     if (this.dataSource) {
50361         this.dataSource= Roo.factory(this.dataSource, Roo.data);
50362         this.ds = this.dataSource;
50363         this.ds.xmodule = this.xmodule || false;
50364          
50365     }
50366     
50367     
50368     
50369     if(this.width){
50370         this.container.setWidth(this.width);
50371     }
50372
50373     if(this.height){
50374         this.container.setHeight(this.height);
50375     }
50376     /** @private */
50377         this.addEvents({
50378         // raw events
50379         /**
50380          * @event click
50381          * The raw click event for the entire grid.
50382          * @param {Roo.EventObject} e
50383          */
50384         "click" : true,
50385         /**
50386          * @event dblclick
50387          * The raw dblclick event for the entire grid.
50388          * @param {Roo.EventObject} e
50389          */
50390         "dblclick" : true,
50391         /**
50392          * @event contextmenu
50393          * The raw contextmenu event for the entire grid.
50394          * @param {Roo.EventObject} e
50395          */
50396         "contextmenu" : true,
50397         /**
50398          * @event mousedown
50399          * The raw mousedown event for the entire grid.
50400          * @param {Roo.EventObject} e
50401          */
50402         "mousedown" : true,
50403         /**
50404          * @event mouseup
50405          * The raw mouseup event for the entire grid.
50406          * @param {Roo.EventObject} e
50407          */
50408         "mouseup" : true,
50409         /**
50410          * @event mouseover
50411          * The raw mouseover event for the entire grid.
50412          * @param {Roo.EventObject} e
50413          */
50414         "mouseover" : true,
50415         /**
50416          * @event mouseout
50417          * The raw mouseout event for the entire grid.
50418          * @param {Roo.EventObject} e
50419          */
50420         "mouseout" : true,
50421         /**
50422          * @event keypress
50423          * The raw keypress event for the entire grid.
50424          * @param {Roo.EventObject} e
50425          */
50426         "keypress" : true,
50427         /**
50428          * @event keydown
50429          * The raw keydown event for the entire grid.
50430          * @param {Roo.EventObject} e
50431          */
50432         "keydown" : true,
50433
50434         // custom events
50435
50436         /**
50437          * @event cellclick
50438          * Fires when a cell is clicked
50439          * @param {Grid} this
50440          * @param {Number} rowIndex
50441          * @param {Number} columnIndex
50442          * @param {Roo.EventObject} e
50443          */
50444         "cellclick" : true,
50445         /**
50446          * @event celldblclick
50447          * Fires when a cell is double clicked
50448          * @param {Grid} this
50449          * @param {Number} rowIndex
50450          * @param {Number} columnIndex
50451          * @param {Roo.EventObject} e
50452          */
50453         "celldblclick" : true,
50454         /**
50455          * @event rowclick
50456          * Fires when a row is clicked
50457          * @param {Grid} this
50458          * @param {Number} rowIndex
50459          * @param {Roo.EventObject} e
50460          */
50461         "rowclick" : true,
50462         /**
50463          * @event rowdblclick
50464          * Fires when a row is double clicked
50465          * @param {Grid} this
50466          * @param {Number} rowIndex
50467          * @param {Roo.EventObject} e
50468          */
50469         "rowdblclick" : true,
50470         /**
50471          * @event headerclick
50472          * Fires when a header is clicked
50473          * @param {Grid} this
50474          * @param {Number} columnIndex
50475          * @param {Roo.EventObject} e
50476          */
50477         "headerclick" : true,
50478         /**
50479          * @event headerdblclick
50480          * Fires when a header cell is double clicked
50481          * @param {Grid} this
50482          * @param {Number} columnIndex
50483          * @param {Roo.EventObject} e
50484          */
50485         "headerdblclick" : true,
50486         /**
50487          * @event rowcontextmenu
50488          * Fires when a row is right clicked
50489          * @param {Grid} this
50490          * @param {Number} rowIndex
50491          * @param {Roo.EventObject} e
50492          */
50493         "rowcontextmenu" : true,
50494         /**
50495          * @event cellcontextmenu
50496          * Fires when a cell is right clicked
50497          * @param {Grid} this
50498          * @param {Number} rowIndex
50499          * @param {Number} cellIndex
50500          * @param {Roo.EventObject} e
50501          */
50502          "cellcontextmenu" : true,
50503         /**
50504          * @event headercontextmenu
50505          * Fires when a header is right clicked
50506          * @param {Grid} this
50507          * @param {Number} columnIndex
50508          * @param {Roo.EventObject} e
50509          */
50510         "headercontextmenu" : true,
50511         /**
50512          * @event bodyscroll
50513          * Fires when the body element is scrolled
50514          * @param {Number} scrollLeft
50515          * @param {Number} scrollTop
50516          */
50517         "bodyscroll" : true,
50518         /**
50519          * @event columnresize
50520          * Fires when the user resizes a column
50521          * @param {Number} columnIndex
50522          * @param {Number} newSize
50523          */
50524         "columnresize" : true,
50525         /**
50526          * @event columnmove
50527          * Fires when the user moves a column
50528          * @param {Number} oldIndex
50529          * @param {Number} newIndex
50530          */
50531         "columnmove" : true,
50532         /**
50533          * @event startdrag
50534          * Fires when row(s) start being dragged
50535          * @param {Grid} this
50536          * @param {Roo.GridDD} dd The drag drop object
50537          * @param {event} e The raw browser event
50538          */
50539         "startdrag" : true,
50540         /**
50541          * @event enddrag
50542          * Fires when a drag operation is complete
50543          * @param {Grid} this
50544          * @param {Roo.GridDD} dd The drag drop object
50545          * @param {event} e The raw browser event
50546          */
50547         "enddrag" : true,
50548         /**
50549          * @event dragdrop
50550          * Fires when dragged row(s) are dropped on a valid DD target
50551          * @param {Grid} this
50552          * @param {Roo.GridDD} dd The drag drop object
50553          * @param {String} targetId The target drag drop object
50554          * @param {event} e The raw browser event
50555          */
50556         "dragdrop" : true,
50557         /**
50558          * @event dragover
50559          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
50560          * @param {Grid} this
50561          * @param {Roo.GridDD} dd The drag drop object
50562          * @param {String} targetId The target drag drop object
50563          * @param {event} e The raw browser event
50564          */
50565         "dragover" : true,
50566         /**
50567          * @event dragenter
50568          *  Fires when the dragged row(s) first cross another DD target while being dragged
50569          * @param {Grid} this
50570          * @param {Roo.GridDD} dd The drag drop object
50571          * @param {String} targetId The target drag drop object
50572          * @param {event} e The raw browser event
50573          */
50574         "dragenter" : true,
50575         /**
50576          * @event dragout
50577          * Fires when the dragged row(s) leave another DD target while being dragged
50578          * @param {Grid} this
50579          * @param {Roo.GridDD} dd The drag drop object
50580          * @param {String} targetId The target drag drop object
50581          * @param {event} e The raw browser event
50582          */
50583         "dragout" : true,
50584         /**
50585          * @event rowclass
50586          * Fires when a row is rendered, so you can change add a style to it.
50587          * @param {GridView} gridview   The grid view
50588          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
50589          */
50590         'rowclass' : true,
50591
50592         /**
50593          * @event render
50594          * Fires when the grid is rendered
50595          * @param {Grid} grid
50596          */
50597         'render' : true
50598     });
50599
50600     Roo.grid.Grid.superclass.constructor.call(this);
50601 };
50602 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
50603     
50604     /**
50605      * @cfg {String} ddGroup - drag drop group.
50606      */
50607
50608     /**
50609      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
50610      */
50611     minColumnWidth : 25,
50612
50613     /**
50614      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
50615      * <b>on initial render.</b> It is more efficient to explicitly size the columns
50616      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
50617      */
50618     autoSizeColumns : false,
50619
50620     /**
50621      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
50622      */
50623     autoSizeHeaders : true,
50624
50625     /**
50626      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
50627      */
50628     monitorWindowResize : true,
50629
50630     /**
50631      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
50632      * rows measured to get a columns size. Default is 0 (all rows).
50633      */
50634     maxRowsToMeasure : 0,
50635
50636     /**
50637      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
50638      */
50639     trackMouseOver : true,
50640
50641     /**
50642     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
50643     */
50644     
50645     /**
50646     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
50647     */
50648     enableDragDrop : false,
50649     
50650     /**
50651     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
50652     */
50653     enableColumnMove : true,
50654     
50655     /**
50656     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
50657     */
50658     enableColumnHide : true,
50659     
50660     /**
50661     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
50662     */
50663     enableRowHeightSync : false,
50664     
50665     /**
50666     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
50667     */
50668     stripeRows : true,
50669     
50670     /**
50671     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
50672     */
50673     autoHeight : false,
50674
50675     /**
50676      * @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.
50677      */
50678     autoExpandColumn : false,
50679
50680     /**
50681     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
50682     * Default is 50.
50683     */
50684     autoExpandMin : 50,
50685
50686     /**
50687     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
50688     */
50689     autoExpandMax : 1000,
50690
50691     /**
50692     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
50693     */
50694     view : null,
50695
50696     /**
50697     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
50698     */
50699     loadMask : false,
50700     /**
50701     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
50702     */
50703     dropTarget: false,
50704     
50705    
50706     
50707     // private
50708     rendered : false,
50709
50710     /**
50711     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
50712     * of a fixed width. Default is false.
50713     */
50714     /**
50715     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
50716     */
50717     /**
50718      * Called once after all setup has been completed and the grid is ready to be rendered.
50719      * @return {Roo.grid.Grid} this
50720      */
50721     render : function()
50722     {
50723         var c = this.container;
50724         // try to detect autoHeight/width mode
50725         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
50726             this.autoHeight = true;
50727         }
50728         var view = this.getView();
50729         view.init(this);
50730
50731         c.on("click", this.onClick, this);
50732         c.on("dblclick", this.onDblClick, this);
50733         c.on("contextmenu", this.onContextMenu, this);
50734         c.on("keydown", this.onKeyDown, this);
50735         if (Roo.isTouch) {
50736             c.on("touch", this.onTouch, this);
50737         }
50738
50739         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
50740
50741         this.getSelectionModel().init(this);
50742
50743         view.render();
50744
50745         if(this.loadMask){
50746             this.loadMask = new Roo.LoadMask(this.container,
50747                     Roo.apply({store:this.dataSource}, this.loadMask));
50748         }
50749         
50750         
50751         if (this.toolbar && this.toolbar.xtype) {
50752             this.toolbar.container = this.getView().getHeaderPanel(true);
50753             this.toolbar = new Roo.Toolbar(this.toolbar);
50754         }
50755         if (this.footer && this.footer.xtype) {
50756             this.footer.dataSource = this.getDataSource();
50757             this.footer.container = this.getView().getFooterPanel(true);
50758             this.footer = Roo.factory(this.footer, Roo);
50759         }
50760         if (this.dropTarget && this.dropTarget.xtype) {
50761             delete this.dropTarget.xtype;
50762             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
50763         }
50764         
50765         
50766         this.rendered = true;
50767         this.fireEvent('render', this);
50768         return this;
50769     },
50770
50771         /**
50772          * Reconfigures the grid to use a different Store and Column Model.
50773          * The View will be bound to the new objects and refreshed.
50774          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
50775          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
50776          */
50777     reconfigure : function(dataSource, colModel){
50778         if(this.loadMask){
50779             this.loadMask.destroy();
50780             this.loadMask = new Roo.LoadMask(this.container,
50781                     Roo.apply({store:dataSource}, this.loadMask));
50782         }
50783         this.view.bind(dataSource, colModel);
50784         this.dataSource = dataSource;
50785         this.colModel = colModel;
50786         this.view.refresh(true);
50787     },
50788
50789     // private
50790     onKeyDown : function(e){
50791         this.fireEvent("keydown", e);
50792     },
50793
50794     /**
50795      * Destroy this grid.
50796      * @param {Boolean} removeEl True to remove the element
50797      */
50798     destroy : function(removeEl, keepListeners){
50799         if(this.loadMask){
50800             this.loadMask.destroy();
50801         }
50802         var c = this.container;
50803         c.removeAllListeners();
50804         this.view.destroy();
50805         this.colModel.purgeListeners();
50806         if(!keepListeners){
50807             this.purgeListeners();
50808         }
50809         c.update("");
50810         if(removeEl === true){
50811             c.remove();
50812         }
50813     },
50814
50815     // private
50816     processEvent : function(name, e){
50817         // does this fire select???
50818         this.fireEvent(name == 'touch' ? 'click' : name, e);
50819         var t = e.getTarget();
50820         var v = this.view;
50821         var header = v.findHeaderIndex(t);
50822         if(header !== false){
50823             this.fireEvent("header" + name, this, header, e);
50824         }else{
50825             var row = v.findRowIndex(t);
50826             var cell = v.findCellIndex(t);
50827             if (name == 'touch') {
50828                 // first touch is always a click.
50829                 // hopefull this happens after selection is updated.?
50830                 name = 'click';
50831                 
50832                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
50833                     var cs = this.selModel.getSelectedCell();
50834                     if (row == cs[0] && cell == cs[1]){
50835                         name = 'dblclick';
50836                     }
50837                 }
50838                 if (typeof(this.selModel.getSelections) != 'undefined') {
50839                     var cs = this.selModel.getSelections();
50840                     var ds = this.dataSource;
50841                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
50842                         name = 'dblclick';
50843                     }
50844                 }
50845                 
50846             }
50847             
50848             
50849             if(row !== false){
50850                 this.fireEvent("row" + name, this, row, e);
50851                 if(cell !== false){
50852                     this.fireEvent("cell" + name, this, row, cell, e);
50853                 }
50854             }
50855         }
50856     },
50857
50858     // private
50859     onClick : function(e){
50860         this.processEvent("click", e);
50861     },
50862    // private
50863     onTouch : function(e){
50864         this.processEvent("touch", e);
50865     },
50866
50867     // private
50868     onContextMenu : function(e, t){
50869         this.processEvent("contextmenu", e);
50870     },
50871
50872     // private
50873     onDblClick : function(e){
50874         this.processEvent("dblclick", e);
50875     },
50876
50877     // private
50878     walkCells : function(row, col, step, fn, scope){
50879         var cm = this.colModel, clen = cm.getColumnCount();
50880         var ds = this.dataSource, rlen = ds.getCount(), first = true;
50881         if(step < 0){
50882             if(col < 0){
50883                 row--;
50884                 first = false;
50885             }
50886             while(row >= 0){
50887                 if(!first){
50888                     col = clen-1;
50889                 }
50890                 first = false;
50891                 while(col >= 0){
50892                     if(fn.call(scope || this, row, col, cm) === true){
50893                         return [row, col];
50894                     }
50895                     col--;
50896                 }
50897                 row--;
50898             }
50899         } else {
50900             if(col >= clen){
50901                 row++;
50902                 first = false;
50903             }
50904             while(row < rlen){
50905                 if(!first){
50906                     col = 0;
50907                 }
50908                 first = false;
50909                 while(col < clen){
50910                     if(fn.call(scope || this, row, col, cm) === true){
50911                         return [row, col];
50912                     }
50913                     col++;
50914                 }
50915                 row++;
50916             }
50917         }
50918         return null;
50919     },
50920
50921     // private
50922     getSelections : function(){
50923         return this.selModel.getSelections();
50924     },
50925
50926     /**
50927      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
50928      * but if manual update is required this method will initiate it.
50929      */
50930     autoSize : function(){
50931         if(this.rendered){
50932             this.view.layout();
50933             if(this.view.adjustForScroll){
50934                 this.view.adjustForScroll();
50935             }
50936         }
50937     },
50938
50939     /**
50940      * Returns the grid's underlying element.
50941      * @return {Element} The element
50942      */
50943     getGridEl : function(){
50944         return this.container;
50945     },
50946
50947     // private for compatibility, overridden by editor grid
50948     stopEditing : function(){},
50949
50950     /**
50951      * Returns the grid's SelectionModel.
50952      * @return {SelectionModel}
50953      */
50954     getSelectionModel : function(){
50955         if(!this.selModel){
50956             this.selModel = new Roo.grid.RowSelectionModel();
50957         }
50958         return this.selModel;
50959     },
50960
50961     /**
50962      * Returns the grid's DataSource.
50963      * @return {DataSource}
50964      */
50965     getDataSource : function(){
50966         return this.dataSource;
50967     },
50968
50969     /**
50970      * Returns the grid's ColumnModel.
50971      * @return {ColumnModel}
50972      */
50973     getColumnModel : function(){
50974         return this.colModel;
50975     },
50976
50977     /**
50978      * Returns the grid's GridView object.
50979      * @return {GridView}
50980      */
50981     getView : function(){
50982         if(!this.view){
50983             this.view = new Roo.grid.GridView(this.viewConfig);
50984         }
50985         return this.view;
50986     },
50987     /**
50988      * Called to get grid's drag proxy text, by default returns this.ddText.
50989      * @return {String}
50990      */
50991     getDragDropText : function(){
50992         var count = this.selModel.getCount();
50993         return String.format(this.ddText, count, count == 1 ? '' : 's');
50994     }
50995 });
50996 /**
50997  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
50998  * %0 is replaced with the number of selected rows.
50999  * @type String
51000  */
51001 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
51002  * Based on:
51003  * Ext JS Library 1.1.1
51004  * Copyright(c) 2006-2007, Ext JS, LLC.
51005  *
51006  * Originally Released Under LGPL - original licence link has changed is not relivant.
51007  *
51008  * Fork - LGPL
51009  * <script type="text/javascript">
51010  */
51011  
51012 Roo.grid.AbstractGridView = function(){
51013         this.grid = null;
51014         
51015         this.events = {
51016             "beforerowremoved" : true,
51017             "beforerowsinserted" : true,
51018             "beforerefresh" : true,
51019             "rowremoved" : true,
51020             "rowsinserted" : true,
51021             "rowupdated" : true,
51022             "refresh" : true
51023         };
51024     Roo.grid.AbstractGridView.superclass.constructor.call(this);
51025 };
51026
51027 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
51028     rowClass : "x-grid-row",
51029     cellClass : "x-grid-cell",
51030     tdClass : "x-grid-td",
51031     hdClass : "x-grid-hd",
51032     splitClass : "x-grid-hd-split",
51033     
51034         init: function(grid){
51035         this.grid = grid;
51036                 var cid = this.grid.getGridEl().id;
51037         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
51038         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
51039         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
51040         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
51041         },
51042         
51043         getColumnRenderers : function(){
51044         var renderers = [];
51045         var cm = this.grid.colModel;
51046         var colCount = cm.getColumnCount();
51047         for(var i = 0; i < colCount; i++){
51048             renderers[i] = cm.getRenderer(i);
51049         }
51050         return renderers;
51051     },
51052     
51053     getColumnIds : function(){
51054         var ids = [];
51055         var cm = this.grid.colModel;
51056         var colCount = cm.getColumnCount();
51057         for(var i = 0; i < colCount; i++){
51058             ids[i] = cm.getColumnId(i);
51059         }
51060         return ids;
51061     },
51062     
51063     getDataIndexes : function(){
51064         if(!this.indexMap){
51065             this.indexMap = this.buildIndexMap();
51066         }
51067         return this.indexMap.colToData;
51068     },
51069     
51070     getColumnIndexByDataIndex : function(dataIndex){
51071         if(!this.indexMap){
51072             this.indexMap = this.buildIndexMap();
51073         }
51074         return this.indexMap.dataToCol[dataIndex];
51075     },
51076     
51077     /**
51078      * Set a css style for a column dynamically. 
51079      * @param {Number} colIndex The index of the column
51080      * @param {String} name The css property name
51081      * @param {String} value The css value
51082      */
51083     setCSSStyle : function(colIndex, name, value){
51084         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
51085         Roo.util.CSS.updateRule(selector, name, value);
51086     },
51087     
51088     generateRules : function(cm){
51089         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
51090         Roo.util.CSS.removeStyleSheet(rulesId);
51091         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51092             var cid = cm.getColumnId(i);
51093             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
51094                          this.tdSelector, cid, " {\n}\n",
51095                          this.hdSelector, cid, " {\n}\n",
51096                          this.splitSelector, cid, " {\n}\n");
51097         }
51098         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
51099     }
51100 });/*
51101  * Based on:
51102  * Ext JS Library 1.1.1
51103  * Copyright(c) 2006-2007, Ext JS, LLC.
51104  *
51105  * Originally Released Under LGPL - original licence link has changed is not relivant.
51106  *
51107  * Fork - LGPL
51108  * <script type="text/javascript">
51109  */
51110
51111 // private
51112 // This is a support class used internally by the Grid components
51113 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
51114     this.grid = grid;
51115     this.view = grid.getView();
51116     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51117     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
51118     if(hd2){
51119         this.setHandleElId(Roo.id(hd));
51120         this.setOuterHandleElId(Roo.id(hd2));
51121     }
51122     this.scroll = false;
51123 };
51124 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
51125     maxDragWidth: 120,
51126     getDragData : function(e){
51127         var t = Roo.lib.Event.getTarget(e);
51128         var h = this.view.findHeaderCell(t);
51129         if(h){
51130             return {ddel: h.firstChild, header:h};
51131         }
51132         return false;
51133     },
51134
51135     onInitDrag : function(e){
51136         this.view.headersDisabled = true;
51137         var clone = this.dragData.ddel.cloneNode(true);
51138         clone.id = Roo.id();
51139         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
51140         this.proxy.update(clone);
51141         return true;
51142     },
51143
51144     afterValidDrop : function(){
51145         var v = this.view;
51146         setTimeout(function(){
51147             v.headersDisabled = false;
51148         }, 50);
51149     },
51150
51151     afterInvalidDrop : function(){
51152         var v = this.view;
51153         setTimeout(function(){
51154             v.headersDisabled = false;
51155         }, 50);
51156     }
51157 });
51158 /*
51159  * Based on:
51160  * Ext JS Library 1.1.1
51161  * Copyright(c) 2006-2007, Ext JS, LLC.
51162  *
51163  * Originally Released Under LGPL - original licence link has changed is not relivant.
51164  *
51165  * Fork - LGPL
51166  * <script type="text/javascript">
51167  */
51168 // private
51169 // This is a support class used internally by the Grid components
51170 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
51171     this.grid = grid;
51172     this.view = grid.getView();
51173     // split the proxies so they don't interfere with mouse events
51174     this.proxyTop = Roo.DomHelper.append(document.body, {
51175         cls:"col-move-top", html:"&#160;"
51176     }, true);
51177     this.proxyBottom = Roo.DomHelper.append(document.body, {
51178         cls:"col-move-bottom", html:"&#160;"
51179     }, true);
51180     this.proxyTop.hide = this.proxyBottom.hide = function(){
51181         this.setLeftTop(-100,-100);
51182         this.setStyle("visibility", "hidden");
51183     };
51184     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51185     // temporarily disabled
51186     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
51187     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
51188 };
51189 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
51190     proxyOffsets : [-4, -9],
51191     fly: Roo.Element.fly,
51192
51193     getTargetFromEvent : function(e){
51194         var t = Roo.lib.Event.getTarget(e);
51195         var cindex = this.view.findCellIndex(t);
51196         if(cindex !== false){
51197             return this.view.getHeaderCell(cindex);
51198         }
51199         return null;
51200     },
51201
51202     nextVisible : function(h){
51203         var v = this.view, cm = this.grid.colModel;
51204         h = h.nextSibling;
51205         while(h){
51206             if(!cm.isHidden(v.getCellIndex(h))){
51207                 return h;
51208             }
51209             h = h.nextSibling;
51210         }
51211         return null;
51212     },
51213
51214     prevVisible : function(h){
51215         var v = this.view, cm = this.grid.colModel;
51216         h = h.prevSibling;
51217         while(h){
51218             if(!cm.isHidden(v.getCellIndex(h))){
51219                 return h;
51220             }
51221             h = h.prevSibling;
51222         }
51223         return null;
51224     },
51225
51226     positionIndicator : function(h, n, e){
51227         var x = Roo.lib.Event.getPageX(e);
51228         var r = Roo.lib.Dom.getRegion(n.firstChild);
51229         var px, pt, py = r.top + this.proxyOffsets[1];
51230         if((r.right - x) <= (r.right-r.left)/2){
51231             px = r.right+this.view.borderWidth;
51232             pt = "after";
51233         }else{
51234             px = r.left;
51235             pt = "before";
51236         }
51237         var oldIndex = this.view.getCellIndex(h);
51238         var newIndex = this.view.getCellIndex(n);
51239
51240         if(this.grid.colModel.isFixed(newIndex)){
51241             return false;
51242         }
51243
51244         var locked = this.grid.colModel.isLocked(newIndex);
51245
51246         if(pt == "after"){
51247             newIndex++;
51248         }
51249         if(oldIndex < newIndex){
51250             newIndex--;
51251         }
51252         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
51253             return false;
51254         }
51255         px +=  this.proxyOffsets[0];
51256         this.proxyTop.setLeftTop(px, py);
51257         this.proxyTop.show();
51258         if(!this.bottomOffset){
51259             this.bottomOffset = this.view.mainHd.getHeight();
51260         }
51261         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
51262         this.proxyBottom.show();
51263         return pt;
51264     },
51265
51266     onNodeEnter : function(n, dd, e, data){
51267         if(data.header != n){
51268             this.positionIndicator(data.header, n, e);
51269         }
51270     },
51271
51272     onNodeOver : function(n, dd, e, data){
51273         var result = false;
51274         if(data.header != n){
51275             result = this.positionIndicator(data.header, n, e);
51276         }
51277         if(!result){
51278             this.proxyTop.hide();
51279             this.proxyBottom.hide();
51280         }
51281         return result ? this.dropAllowed : this.dropNotAllowed;
51282     },
51283
51284     onNodeOut : function(n, dd, e, data){
51285         this.proxyTop.hide();
51286         this.proxyBottom.hide();
51287     },
51288
51289     onNodeDrop : function(n, dd, e, data){
51290         var h = data.header;
51291         if(h != n){
51292             var cm = this.grid.colModel;
51293             var x = Roo.lib.Event.getPageX(e);
51294             var r = Roo.lib.Dom.getRegion(n.firstChild);
51295             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
51296             var oldIndex = this.view.getCellIndex(h);
51297             var newIndex = this.view.getCellIndex(n);
51298             var locked = cm.isLocked(newIndex);
51299             if(pt == "after"){
51300                 newIndex++;
51301             }
51302             if(oldIndex < newIndex){
51303                 newIndex--;
51304             }
51305             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
51306                 return false;
51307             }
51308             cm.setLocked(oldIndex, locked, true);
51309             cm.moveColumn(oldIndex, newIndex);
51310             this.grid.fireEvent("columnmove", oldIndex, newIndex);
51311             return true;
51312         }
51313         return false;
51314     }
51315 });
51316 /*
51317  * Based on:
51318  * Ext JS Library 1.1.1
51319  * Copyright(c) 2006-2007, Ext JS, LLC.
51320  *
51321  * Originally Released Under LGPL - original licence link has changed is not relivant.
51322  *
51323  * Fork - LGPL
51324  * <script type="text/javascript">
51325  */
51326   
51327 /**
51328  * @class Roo.grid.GridView
51329  * @extends Roo.util.Observable
51330  *
51331  * @constructor
51332  * @param {Object} config
51333  */
51334 Roo.grid.GridView = function(config){
51335     Roo.grid.GridView.superclass.constructor.call(this);
51336     this.el = null;
51337
51338     Roo.apply(this, config);
51339 };
51340
51341 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
51342
51343     unselectable :  'unselectable="on"',
51344     unselectableCls :  'x-unselectable',
51345     
51346     
51347     rowClass : "x-grid-row",
51348
51349     cellClass : "x-grid-col",
51350
51351     tdClass : "x-grid-td",
51352
51353     hdClass : "x-grid-hd",
51354
51355     splitClass : "x-grid-split",
51356
51357     sortClasses : ["sort-asc", "sort-desc"],
51358
51359     enableMoveAnim : false,
51360
51361     hlColor: "C3DAF9",
51362
51363     dh : Roo.DomHelper,
51364
51365     fly : Roo.Element.fly,
51366
51367     css : Roo.util.CSS,
51368
51369     borderWidth: 1,
51370
51371     splitOffset: 3,
51372
51373     scrollIncrement : 22,
51374
51375     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
51376
51377     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
51378
51379     bind : function(ds, cm){
51380         if(this.ds){
51381             this.ds.un("load", this.onLoad, this);
51382             this.ds.un("datachanged", this.onDataChange, this);
51383             this.ds.un("add", this.onAdd, this);
51384             this.ds.un("remove", this.onRemove, this);
51385             this.ds.un("update", this.onUpdate, this);
51386             this.ds.un("clear", this.onClear, this);
51387         }
51388         if(ds){
51389             ds.on("load", this.onLoad, this);
51390             ds.on("datachanged", this.onDataChange, this);
51391             ds.on("add", this.onAdd, this);
51392             ds.on("remove", this.onRemove, this);
51393             ds.on("update", this.onUpdate, this);
51394             ds.on("clear", this.onClear, this);
51395         }
51396         this.ds = ds;
51397
51398         if(this.cm){
51399             this.cm.un("widthchange", this.onColWidthChange, this);
51400             this.cm.un("headerchange", this.onHeaderChange, this);
51401             this.cm.un("hiddenchange", this.onHiddenChange, this);
51402             this.cm.un("columnmoved", this.onColumnMove, this);
51403             this.cm.un("columnlockchange", this.onColumnLock, this);
51404         }
51405         if(cm){
51406             this.generateRules(cm);
51407             cm.on("widthchange", this.onColWidthChange, this);
51408             cm.on("headerchange", this.onHeaderChange, this);
51409             cm.on("hiddenchange", this.onHiddenChange, this);
51410             cm.on("columnmoved", this.onColumnMove, this);
51411             cm.on("columnlockchange", this.onColumnLock, this);
51412         }
51413         this.cm = cm;
51414     },
51415
51416     init: function(grid){
51417         Roo.grid.GridView.superclass.init.call(this, grid);
51418
51419         this.bind(grid.dataSource, grid.colModel);
51420
51421         grid.on("headerclick", this.handleHeaderClick, this);
51422
51423         if(grid.trackMouseOver){
51424             grid.on("mouseover", this.onRowOver, this);
51425             grid.on("mouseout", this.onRowOut, this);
51426         }
51427         grid.cancelTextSelection = function(){};
51428         this.gridId = grid.id;
51429
51430         var tpls = this.templates || {};
51431
51432         if(!tpls.master){
51433             tpls.master = new Roo.Template(
51434                '<div class="x-grid" hidefocus="true">',
51435                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
51436                   '<div class="x-grid-topbar"></div>',
51437                   '<div class="x-grid-scroller"><div></div></div>',
51438                   '<div class="x-grid-locked">',
51439                       '<div class="x-grid-header">{lockedHeader}</div>',
51440                       '<div class="x-grid-body">{lockedBody}</div>',
51441                   "</div>",
51442                   '<div class="x-grid-viewport">',
51443                       '<div class="x-grid-header">{header}</div>',
51444                       '<div class="x-grid-body">{body}</div>',
51445                   "</div>",
51446                   '<div class="x-grid-bottombar"></div>',
51447                  
51448                   '<div class="x-grid-resize-proxy">&#160;</div>',
51449                "</div>"
51450             );
51451             tpls.master.disableformats = true;
51452         }
51453
51454         if(!tpls.header){
51455             tpls.header = new Roo.Template(
51456                '<table border="0" cellspacing="0" cellpadding="0">',
51457                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
51458                "</table>{splits}"
51459             );
51460             tpls.header.disableformats = true;
51461         }
51462         tpls.header.compile();
51463
51464         if(!tpls.hcell){
51465             tpls.hcell = new Roo.Template(
51466                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
51467                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
51468                 "</div></td>"
51469              );
51470              tpls.hcell.disableFormats = true;
51471         }
51472         tpls.hcell.compile();
51473
51474         if(!tpls.hsplit){
51475             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
51476                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
51477             tpls.hsplit.disableFormats = true;
51478         }
51479         tpls.hsplit.compile();
51480
51481         if(!tpls.body){
51482             tpls.body = new Roo.Template(
51483                '<table border="0" cellspacing="0" cellpadding="0">',
51484                "<tbody>{rows}</tbody>",
51485                "</table>"
51486             );
51487             tpls.body.disableFormats = true;
51488         }
51489         tpls.body.compile();
51490
51491         if(!tpls.row){
51492             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
51493             tpls.row.disableFormats = true;
51494         }
51495         tpls.row.compile();
51496
51497         if(!tpls.cell){
51498             tpls.cell = new Roo.Template(
51499                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
51500                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
51501                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
51502                 "</td>"
51503             );
51504             tpls.cell.disableFormats = true;
51505         }
51506         tpls.cell.compile();
51507
51508         this.templates = tpls;
51509     },
51510
51511     // remap these for backwards compat
51512     onColWidthChange : function(){
51513         this.updateColumns.apply(this, arguments);
51514     },
51515     onHeaderChange : function(){
51516         this.updateHeaders.apply(this, arguments);
51517     }, 
51518     onHiddenChange : function(){
51519         this.handleHiddenChange.apply(this, arguments);
51520     },
51521     onColumnMove : function(){
51522         this.handleColumnMove.apply(this, arguments);
51523     },
51524     onColumnLock : function(){
51525         this.handleLockChange.apply(this, arguments);
51526     },
51527
51528     onDataChange : function(){
51529         this.refresh();
51530         this.updateHeaderSortState();
51531     },
51532
51533     onClear : function(){
51534         this.refresh();
51535     },
51536
51537     onUpdate : function(ds, record){
51538         this.refreshRow(record);
51539     },
51540
51541     refreshRow : function(record){
51542         var ds = this.ds, index;
51543         if(typeof record == 'number'){
51544             index = record;
51545             record = ds.getAt(index);
51546         }else{
51547             index = ds.indexOf(record);
51548         }
51549         this.insertRows(ds, index, index, true);
51550         this.onRemove(ds, record, index+1, true);
51551         this.syncRowHeights(index, index);
51552         this.layout();
51553         this.fireEvent("rowupdated", this, index, record);
51554     },
51555
51556     onAdd : function(ds, records, index){
51557         this.insertRows(ds, index, index + (records.length-1));
51558     },
51559
51560     onRemove : function(ds, record, index, isUpdate){
51561         if(isUpdate !== true){
51562             this.fireEvent("beforerowremoved", this, index, record);
51563         }
51564         var bt = this.getBodyTable(), lt = this.getLockedTable();
51565         if(bt.rows[index]){
51566             bt.firstChild.removeChild(bt.rows[index]);
51567         }
51568         if(lt.rows[index]){
51569             lt.firstChild.removeChild(lt.rows[index]);
51570         }
51571         if(isUpdate !== true){
51572             this.stripeRows(index);
51573             this.syncRowHeights(index, index);
51574             this.layout();
51575             this.fireEvent("rowremoved", this, index, record);
51576         }
51577     },
51578
51579     onLoad : function(){
51580         this.scrollToTop();
51581     },
51582
51583     /**
51584      * Scrolls the grid to the top
51585      */
51586     scrollToTop : function(){
51587         if(this.scroller){
51588             this.scroller.dom.scrollTop = 0;
51589             this.syncScroll();
51590         }
51591     },
51592
51593     /**
51594      * Gets a panel in the header of the grid that can be used for toolbars etc.
51595      * After modifying the contents of this panel a call to grid.autoSize() may be
51596      * required to register any changes in size.
51597      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
51598      * @return Roo.Element
51599      */
51600     getHeaderPanel : function(doShow){
51601         if(doShow){
51602             this.headerPanel.show();
51603         }
51604         return this.headerPanel;
51605     },
51606
51607     /**
51608      * Gets a panel in the footer of the grid that can be used for toolbars etc.
51609      * After modifying the contents of this panel a call to grid.autoSize() may be
51610      * required to register any changes in size.
51611      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
51612      * @return Roo.Element
51613      */
51614     getFooterPanel : function(doShow){
51615         if(doShow){
51616             this.footerPanel.show();
51617         }
51618         return this.footerPanel;
51619     },
51620
51621     initElements : function(){
51622         var E = Roo.Element;
51623         var el = this.grid.getGridEl().dom.firstChild;
51624         var cs = el.childNodes;
51625
51626         this.el = new E(el);
51627         
51628          this.focusEl = new E(el.firstChild);
51629         this.focusEl.swallowEvent("click", true);
51630         
51631         this.headerPanel = new E(cs[1]);
51632         this.headerPanel.enableDisplayMode("block");
51633
51634         this.scroller = new E(cs[2]);
51635         this.scrollSizer = new E(this.scroller.dom.firstChild);
51636
51637         this.lockedWrap = new E(cs[3]);
51638         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
51639         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
51640
51641         this.mainWrap = new E(cs[4]);
51642         this.mainHd = new E(this.mainWrap.dom.firstChild);
51643         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
51644
51645         this.footerPanel = new E(cs[5]);
51646         this.footerPanel.enableDisplayMode("block");
51647
51648         this.resizeProxy = new E(cs[6]);
51649
51650         this.headerSelector = String.format(
51651            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
51652            this.lockedHd.id, this.mainHd.id
51653         );
51654
51655         this.splitterSelector = String.format(
51656            '#{0} div.x-grid-split, #{1} div.x-grid-split',
51657            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
51658         );
51659     },
51660     idToCssName : function(s)
51661     {
51662         return s.replace(/[^a-z0-9]+/ig, '-');
51663     },
51664
51665     getHeaderCell : function(index){
51666         return Roo.DomQuery.select(this.headerSelector)[index];
51667     },
51668
51669     getHeaderCellMeasure : function(index){
51670         return this.getHeaderCell(index).firstChild;
51671     },
51672
51673     getHeaderCellText : function(index){
51674         return this.getHeaderCell(index).firstChild.firstChild;
51675     },
51676
51677     getLockedTable : function(){
51678         return this.lockedBody.dom.firstChild;
51679     },
51680
51681     getBodyTable : function(){
51682         return this.mainBody.dom.firstChild;
51683     },
51684
51685     getLockedRow : function(index){
51686         return this.getLockedTable().rows[index];
51687     },
51688
51689     getRow : function(index){
51690         return this.getBodyTable().rows[index];
51691     },
51692
51693     getRowComposite : function(index){
51694         if(!this.rowEl){
51695             this.rowEl = new Roo.CompositeElementLite();
51696         }
51697         var els = [], lrow, mrow;
51698         if(lrow = this.getLockedRow(index)){
51699             els.push(lrow);
51700         }
51701         if(mrow = this.getRow(index)){
51702             els.push(mrow);
51703         }
51704         this.rowEl.elements = els;
51705         return this.rowEl;
51706     },
51707     /**
51708      * Gets the 'td' of the cell
51709      * 
51710      * @param {Integer} rowIndex row to select
51711      * @param {Integer} colIndex column to select
51712      * 
51713      * @return {Object} 
51714      */
51715     getCell : function(rowIndex, colIndex){
51716         var locked = this.cm.getLockedCount();
51717         var source;
51718         if(colIndex < locked){
51719             source = this.lockedBody.dom.firstChild;
51720         }else{
51721             source = this.mainBody.dom.firstChild;
51722             colIndex -= locked;
51723         }
51724         return source.rows[rowIndex].childNodes[colIndex];
51725     },
51726
51727     getCellText : function(rowIndex, colIndex){
51728         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
51729     },
51730
51731     getCellBox : function(cell){
51732         var b = this.fly(cell).getBox();
51733         if(Roo.isOpera){ // opera fails to report the Y
51734             b.y = cell.offsetTop + this.mainBody.getY();
51735         }
51736         return b;
51737     },
51738
51739     getCellIndex : function(cell){
51740         var id = String(cell.className).match(this.cellRE);
51741         if(id){
51742             return parseInt(id[1], 10);
51743         }
51744         return 0;
51745     },
51746
51747     findHeaderIndex : function(n){
51748         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
51749         return r ? this.getCellIndex(r) : false;
51750     },
51751
51752     findHeaderCell : function(n){
51753         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
51754         return r ? r : false;
51755     },
51756
51757     findRowIndex : function(n){
51758         if(!n){
51759             return false;
51760         }
51761         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
51762         return r ? r.rowIndex : false;
51763     },
51764
51765     findCellIndex : function(node){
51766         var stop = this.el.dom;
51767         while(node && node != stop){
51768             if(this.findRE.test(node.className)){
51769                 return this.getCellIndex(node);
51770             }
51771             node = node.parentNode;
51772         }
51773         return false;
51774     },
51775
51776     getColumnId : function(index){
51777         return this.cm.getColumnId(index);
51778     },
51779
51780     getSplitters : function()
51781     {
51782         if(this.splitterSelector){
51783            return Roo.DomQuery.select(this.splitterSelector);
51784         }else{
51785             return null;
51786       }
51787     },
51788
51789     getSplitter : function(index){
51790         return this.getSplitters()[index];
51791     },
51792
51793     onRowOver : function(e, t){
51794         var row;
51795         if((row = this.findRowIndex(t)) !== false){
51796             this.getRowComposite(row).addClass("x-grid-row-over");
51797         }
51798     },
51799
51800     onRowOut : function(e, t){
51801         var row;
51802         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
51803             this.getRowComposite(row).removeClass("x-grid-row-over");
51804         }
51805     },
51806
51807     renderHeaders : function(){
51808         var cm = this.cm;
51809         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
51810         var cb = [], lb = [], sb = [], lsb = [], p = {};
51811         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51812             p.cellId = "x-grid-hd-0-" + i;
51813             p.splitId = "x-grid-csplit-0-" + i;
51814             p.id = cm.getColumnId(i);
51815             p.title = cm.getColumnTooltip(i) || "";
51816             p.value = cm.getColumnHeader(i) || "";
51817             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
51818             if(!cm.isLocked(i)){
51819                 cb[cb.length] = ct.apply(p);
51820                 sb[sb.length] = st.apply(p);
51821             }else{
51822                 lb[lb.length] = ct.apply(p);
51823                 lsb[lsb.length] = st.apply(p);
51824             }
51825         }
51826         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
51827                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
51828     },
51829
51830     updateHeaders : function(){
51831         var html = this.renderHeaders();
51832         this.lockedHd.update(html[0]);
51833         this.mainHd.update(html[1]);
51834     },
51835
51836     /**
51837      * Focuses the specified row.
51838      * @param {Number} row The row index
51839      */
51840     focusRow : function(row)
51841     {
51842         //Roo.log('GridView.focusRow');
51843         var x = this.scroller.dom.scrollLeft;
51844         this.focusCell(row, 0, false);
51845         this.scroller.dom.scrollLeft = x;
51846     },
51847
51848     /**
51849      * Focuses the specified cell.
51850      * @param {Number} row The row index
51851      * @param {Number} col The column index
51852      * @param {Boolean} hscroll false to disable horizontal scrolling
51853      */
51854     focusCell : function(row, col, hscroll)
51855     {
51856         //Roo.log('GridView.focusCell');
51857         var el = this.ensureVisible(row, col, hscroll);
51858         this.focusEl.alignTo(el, "tl-tl");
51859         if(Roo.isGecko){
51860             this.focusEl.focus();
51861         }else{
51862             this.focusEl.focus.defer(1, this.focusEl);
51863         }
51864     },
51865
51866     /**
51867      * Scrolls the specified cell into view
51868      * @param {Number} row The row index
51869      * @param {Number} col The column index
51870      * @param {Boolean} hscroll false to disable horizontal scrolling
51871      */
51872     ensureVisible : function(row, col, hscroll)
51873     {
51874         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
51875         //return null; //disable for testing.
51876         if(typeof row != "number"){
51877             row = row.rowIndex;
51878         }
51879         if(row < 0 && row >= this.ds.getCount()){
51880             return  null;
51881         }
51882         col = (col !== undefined ? col : 0);
51883         var cm = this.grid.colModel;
51884         while(cm.isHidden(col)){
51885             col++;
51886         }
51887
51888         var el = this.getCell(row, col);
51889         if(!el){
51890             return null;
51891         }
51892         var c = this.scroller.dom;
51893
51894         var ctop = parseInt(el.offsetTop, 10);
51895         var cleft = parseInt(el.offsetLeft, 10);
51896         var cbot = ctop + el.offsetHeight;
51897         var cright = cleft + el.offsetWidth;
51898         
51899         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
51900         var stop = parseInt(c.scrollTop, 10);
51901         var sleft = parseInt(c.scrollLeft, 10);
51902         var sbot = stop + ch;
51903         var sright = sleft + c.clientWidth;
51904         /*
51905         Roo.log('GridView.ensureVisible:' +
51906                 ' ctop:' + ctop +
51907                 ' c.clientHeight:' + c.clientHeight +
51908                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
51909                 ' stop:' + stop +
51910                 ' cbot:' + cbot +
51911                 ' sbot:' + sbot +
51912                 ' ch:' + ch  
51913                 );
51914         */
51915         if(ctop < stop){
51916              c.scrollTop = ctop;
51917             //Roo.log("set scrolltop to ctop DISABLE?");
51918         }else if(cbot > sbot){
51919             //Roo.log("set scrolltop to cbot-ch");
51920             c.scrollTop = cbot-ch;
51921         }
51922         
51923         if(hscroll !== false){
51924             if(cleft < sleft){
51925                 c.scrollLeft = cleft;
51926             }else if(cright > sright){
51927                 c.scrollLeft = cright-c.clientWidth;
51928             }
51929         }
51930          
51931         return el;
51932     },
51933
51934     updateColumns : function(){
51935         this.grid.stopEditing();
51936         var cm = this.grid.colModel, colIds = this.getColumnIds();
51937         //var totalWidth = cm.getTotalWidth();
51938         var pos = 0;
51939         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51940             //if(cm.isHidden(i)) continue;
51941             var w = cm.getColumnWidth(i);
51942             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
51943             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
51944         }
51945         this.updateSplitters();
51946     },
51947
51948     generateRules : function(cm){
51949         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
51950         Roo.util.CSS.removeStyleSheet(rulesId);
51951         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51952             var cid = cm.getColumnId(i);
51953             var align = '';
51954             if(cm.config[i].align){
51955                 align = 'text-align:'+cm.config[i].align+';';
51956             }
51957             var hidden = '';
51958             if(cm.isHidden(i)){
51959                 hidden = 'display:none;';
51960             }
51961             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
51962             ruleBuf.push(
51963                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
51964                     this.hdSelector, cid, " {\n", align, width, "}\n",
51965                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
51966                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
51967         }
51968         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
51969     },
51970
51971     updateSplitters : function(){
51972         var cm = this.cm, s = this.getSplitters();
51973         if(s){ // splitters not created yet
51974             var pos = 0, locked = true;
51975             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51976                 if(cm.isHidden(i)) continue;
51977                 var w = cm.getColumnWidth(i); // make sure it's a number
51978                 if(!cm.isLocked(i) && locked){
51979                     pos = 0;
51980                     locked = false;
51981                 }
51982                 pos += w;
51983                 s[i].style.left = (pos-this.splitOffset) + "px";
51984             }
51985         }
51986     },
51987
51988     handleHiddenChange : function(colModel, colIndex, hidden){
51989         if(hidden){
51990             this.hideColumn(colIndex);
51991         }else{
51992             this.unhideColumn(colIndex);
51993         }
51994     },
51995
51996     hideColumn : function(colIndex){
51997         var cid = this.getColumnId(colIndex);
51998         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
51999         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
52000         if(Roo.isSafari){
52001             this.updateHeaders();
52002         }
52003         this.updateSplitters();
52004         this.layout();
52005     },
52006
52007     unhideColumn : function(colIndex){
52008         var cid = this.getColumnId(colIndex);
52009         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
52010         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
52011
52012         if(Roo.isSafari){
52013             this.updateHeaders();
52014         }
52015         this.updateSplitters();
52016         this.layout();
52017     },
52018
52019     insertRows : function(dm, firstRow, lastRow, isUpdate){
52020         if(firstRow == 0 && lastRow == dm.getCount()-1){
52021             this.refresh();
52022         }else{
52023             if(!isUpdate){
52024                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
52025             }
52026             var s = this.getScrollState();
52027             var markup = this.renderRows(firstRow, lastRow);
52028             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
52029             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
52030             this.restoreScroll(s);
52031             if(!isUpdate){
52032                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
52033                 this.syncRowHeights(firstRow, lastRow);
52034                 this.stripeRows(firstRow);
52035                 this.layout();
52036             }
52037         }
52038     },
52039
52040     bufferRows : function(markup, target, index){
52041         var before = null, trows = target.rows, tbody = target.tBodies[0];
52042         if(index < trows.length){
52043             before = trows[index];
52044         }
52045         var b = document.createElement("div");
52046         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
52047         var rows = b.firstChild.rows;
52048         for(var i = 0, len = rows.length; i < len; i++){
52049             if(before){
52050                 tbody.insertBefore(rows[0], before);
52051             }else{
52052                 tbody.appendChild(rows[0]);
52053             }
52054         }
52055         b.innerHTML = "";
52056         b = null;
52057     },
52058
52059     deleteRows : function(dm, firstRow, lastRow){
52060         if(dm.getRowCount()<1){
52061             this.fireEvent("beforerefresh", this);
52062             this.mainBody.update("");
52063             this.lockedBody.update("");
52064             this.fireEvent("refresh", this);
52065         }else{
52066             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
52067             var bt = this.getBodyTable();
52068             var tbody = bt.firstChild;
52069             var rows = bt.rows;
52070             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
52071                 tbody.removeChild(rows[firstRow]);
52072             }
52073             this.stripeRows(firstRow);
52074             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
52075         }
52076     },
52077
52078     updateRows : function(dataSource, firstRow, lastRow){
52079         var s = this.getScrollState();
52080         this.refresh();
52081         this.restoreScroll(s);
52082     },
52083
52084     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
52085         if(!noRefresh){
52086            this.refresh();
52087         }
52088         this.updateHeaderSortState();
52089     },
52090
52091     getScrollState : function(){
52092         
52093         var sb = this.scroller.dom;
52094         return {left: sb.scrollLeft, top: sb.scrollTop};
52095     },
52096
52097     stripeRows : function(startRow){
52098         if(!this.grid.stripeRows || this.ds.getCount() < 1){
52099             return;
52100         }
52101         startRow = startRow || 0;
52102         var rows = this.getBodyTable().rows;
52103         var lrows = this.getLockedTable().rows;
52104         var cls = ' x-grid-row-alt ';
52105         for(var i = startRow, len = rows.length; i < len; i++){
52106             var row = rows[i], lrow = lrows[i];
52107             var isAlt = ((i+1) % 2 == 0);
52108             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
52109             if(isAlt == hasAlt){
52110                 continue;
52111             }
52112             if(isAlt){
52113                 row.className += " x-grid-row-alt";
52114             }else{
52115                 row.className = row.className.replace("x-grid-row-alt", "");
52116             }
52117             if(lrow){
52118                 lrow.className = row.className;
52119             }
52120         }
52121     },
52122
52123     restoreScroll : function(state){
52124         //Roo.log('GridView.restoreScroll');
52125         var sb = this.scroller.dom;
52126         sb.scrollLeft = state.left;
52127         sb.scrollTop = state.top;
52128         this.syncScroll();
52129     },
52130
52131     syncScroll : function(){
52132         //Roo.log('GridView.syncScroll');
52133         var sb = this.scroller.dom;
52134         var sh = this.mainHd.dom;
52135         var bs = this.mainBody.dom;
52136         var lv = this.lockedBody.dom;
52137         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
52138         lv.scrollTop = bs.scrollTop = sb.scrollTop;
52139     },
52140
52141     handleScroll : function(e){
52142         this.syncScroll();
52143         var sb = this.scroller.dom;
52144         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
52145         e.stopEvent();
52146     },
52147
52148     handleWheel : function(e){
52149         var d = e.getWheelDelta();
52150         this.scroller.dom.scrollTop -= d*22;
52151         // set this here to prevent jumpy scrolling on large tables
52152         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
52153         e.stopEvent();
52154     },
52155
52156     renderRows : function(startRow, endRow){
52157         // pull in all the crap needed to render rows
52158         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
52159         var colCount = cm.getColumnCount();
52160
52161         if(ds.getCount() < 1){
52162             return ["", ""];
52163         }
52164
52165         // build a map for all the columns
52166         var cs = [];
52167         for(var i = 0; i < colCount; i++){
52168             var name = cm.getDataIndex(i);
52169             cs[i] = {
52170                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
52171                 renderer : cm.getRenderer(i),
52172                 id : cm.getColumnId(i),
52173                 locked : cm.isLocked(i)
52174             };
52175         }
52176
52177         startRow = startRow || 0;
52178         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
52179
52180         // records to render
52181         var rs = ds.getRange(startRow, endRow);
52182
52183         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
52184     },
52185
52186     // As much as I hate to duplicate code, this was branched because FireFox really hates
52187     // [].join("") on strings. The performance difference was substantial enough to
52188     // branch this function
52189     doRender : Roo.isGecko ?
52190             function(cs, rs, ds, startRow, colCount, stripe){
52191                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52192                 // buffers
52193                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52194                 
52195                 var hasListener = this.grid.hasListener('rowclass');
52196                 var rowcfg = {};
52197                 for(var j = 0, len = rs.length; j < len; j++){
52198                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
52199                     for(var i = 0; i < colCount; i++){
52200                         c = cs[i];
52201                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52202                         p.id = c.id;
52203                         p.css = p.attr = "";
52204                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52205                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52206                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52207                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52208                         }
52209                         var markup = ct.apply(p);
52210                         if(!c.locked){
52211                             cb+= markup;
52212                         }else{
52213                             lcb+= markup;
52214                         }
52215                     }
52216                     var alt = [];
52217                     if(stripe && ((rowIndex+1) % 2 == 0)){
52218                         alt.push("x-grid-row-alt")
52219                     }
52220                     if(r.dirty){
52221                         alt.push(  " x-grid-dirty-row");
52222                     }
52223                     rp.cells = lcb;
52224                     if(this.getRowClass){
52225                         alt.push(this.getRowClass(r, rowIndex));
52226                     }
52227                     if (hasListener) {
52228                         rowcfg = {
52229                              
52230                             record: r,
52231                             rowIndex : rowIndex,
52232                             rowClass : ''
52233                         }
52234                         this.grid.fireEvent('rowclass', this, rowcfg);
52235                         alt.push(rowcfg.rowClass);
52236                     }
52237                     rp.alt = alt.join(" ");
52238                     lbuf+= rt.apply(rp);
52239                     rp.cells = cb;
52240                     buf+=  rt.apply(rp);
52241                 }
52242                 return [lbuf, buf];
52243             } :
52244             function(cs, rs, ds, startRow, colCount, stripe){
52245                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52246                 // buffers
52247                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52248                 var hasListener = this.grid.hasListener('rowclass');
52249  
52250                 var rowcfg = {};
52251                 for(var j = 0, len = rs.length; j < len; j++){
52252                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
52253                     for(var i = 0; i < colCount; i++){
52254                         c = cs[i];
52255                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52256                         p.id = c.id;
52257                         p.css = p.attr = "";
52258                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52259                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52260                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52261                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52262                         }
52263                         
52264                         var markup = ct.apply(p);
52265                         if(!c.locked){
52266                             cb[cb.length] = markup;
52267                         }else{
52268                             lcb[lcb.length] = markup;
52269                         }
52270                     }
52271                     var alt = [];
52272                     if(stripe && ((rowIndex+1) % 2 == 0)){
52273                         alt.push( "x-grid-row-alt");
52274                     }
52275                     if(r.dirty){
52276                         alt.push(" x-grid-dirty-row");
52277                     }
52278                     rp.cells = lcb;
52279                     if(this.getRowClass){
52280                         alt.push( this.getRowClass(r, rowIndex));
52281                     }
52282                     if (hasListener) {
52283                         rowcfg = {
52284                              
52285                             record: r,
52286                             rowIndex : rowIndex,
52287                             rowClass : ''
52288                         }
52289                         this.grid.fireEvent('rowclass', this, rowcfg);
52290                         alt.push(rowcfg.rowClass);
52291                     }
52292                     rp.alt = alt.join(" ");
52293                     rp.cells = lcb.join("");
52294                     lbuf[lbuf.length] = rt.apply(rp);
52295                     rp.cells = cb.join("");
52296                     buf[buf.length] =  rt.apply(rp);
52297                 }
52298                 return [lbuf.join(""), buf.join("")];
52299             },
52300
52301     renderBody : function(){
52302         var markup = this.renderRows();
52303         var bt = this.templates.body;
52304         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
52305     },
52306
52307     /**
52308      * Refreshes the grid
52309      * @param {Boolean} headersToo
52310      */
52311     refresh : function(headersToo){
52312         this.fireEvent("beforerefresh", this);
52313         this.grid.stopEditing();
52314         var result = this.renderBody();
52315         this.lockedBody.update(result[0]);
52316         this.mainBody.update(result[1]);
52317         if(headersToo === true){
52318             this.updateHeaders();
52319             this.updateColumns();
52320             this.updateSplitters();
52321             this.updateHeaderSortState();
52322         }
52323         this.syncRowHeights();
52324         this.layout();
52325         this.fireEvent("refresh", this);
52326     },
52327
52328     handleColumnMove : function(cm, oldIndex, newIndex){
52329         this.indexMap = null;
52330         var s = this.getScrollState();
52331         this.refresh(true);
52332         this.restoreScroll(s);
52333         this.afterMove(newIndex);
52334     },
52335
52336     afterMove : function(colIndex){
52337         if(this.enableMoveAnim && Roo.enableFx){
52338             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
52339         }
52340         // if multisort - fix sortOrder, and reload..
52341         if (this.grid.dataSource.multiSort) {
52342             // the we can call sort again..
52343             var dm = this.grid.dataSource;
52344             var cm = this.grid.colModel;
52345             var so = [];
52346             for(var i = 0; i < cm.config.length; i++ ) {
52347                 
52348                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
52349                     continue; // dont' bother, it's not in sort list or being set.
52350                 }
52351                 
52352                 so.push(cm.config[i].dataIndex);
52353             };
52354             dm.sortOrder = so;
52355             dm.load(dm.lastOptions);
52356             
52357             
52358         }
52359         
52360     },
52361
52362     updateCell : function(dm, rowIndex, dataIndex){
52363         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
52364         if(typeof colIndex == "undefined"){ // not present in grid
52365             return;
52366         }
52367         var cm = this.grid.colModel;
52368         var cell = this.getCell(rowIndex, colIndex);
52369         var cellText = this.getCellText(rowIndex, colIndex);
52370
52371         var p = {
52372             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
52373             id : cm.getColumnId(colIndex),
52374             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
52375         };
52376         var renderer = cm.getRenderer(colIndex);
52377         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
52378         if(typeof val == "undefined" || val === "") val = "&#160;";
52379         cellText.innerHTML = val;
52380         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
52381         this.syncRowHeights(rowIndex, rowIndex);
52382     },
52383
52384     calcColumnWidth : function(colIndex, maxRowsToMeasure){
52385         var maxWidth = 0;
52386         if(this.grid.autoSizeHeaders){
52387             var h = this.getHeaderCellMeasure(colIndex);
52388             maxWidth = Math.max(maxWidth, h.scrollWidth);
52389         }
52390         var tb, index;
52391         if(this.cm.isLocked(colIndex)){
52392             tb = this.getLockedTable();
52393             index = colIndex;
52394         }else{
52395             tb = this.getBodyTable();
52396             index = colIndex - this.cm.getLockedCount();
52397         }
52398         if(tb && tb.rows){
52399             var rows = tb.rows;
52400             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
52401             for(var i = 0; i < stopIndex; i++){
52402                 var cell = rows[i].childNodes[index].firstChild;
52403                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
52404             }
52405         }
52406         return maxWidth + /*margin for error in IE*/ 5;
52407     },
52408     /**
52409      * Autofit a column to its content.
52410      * @param {Number} colIndex
52411      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
52412      */
52413      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
52414          if(this.cm.isHidden(colIndex)){
52415              return; // can't calc a hidden column
52416          }
52417         if(forceMinSize){
52418             var cid = this.cm.getColumnId(colIndex);
52419             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
52420            if(this.grid.autoSizeHeaders){
52421                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
52422            }
52423         }
52424         var newWidth = this.calcColumnWidth(colIndex);
52425         this.cm.setColumnWidth(colIndex,
52426             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
52427         if(!suppressEvent){
52428             this.grid.fireEvent("columnresize", colIndex, newWidth);
52429         }
52430     },
52431
52432     /**
52433      * Autofits all columns to their content and then expands to fit any extra space in the grid
52434      */
52435      autoSizeColumns : function(){
52436         var cm = this.grid.colModel;
52437         var colCount = cm.getColumnCount();
52438         for(var i = 0; i < colCount; i++){
52439             this.autoSizeColumn(i, true, true);
52440         }
52441         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
52442             this.fitColumns();
52443         }else{
52444             this.updateColumns();
52445             this.layout();
52446         }
52447     },
52448
52449     /**
52450      * Autofits all columns to the grid's width proportionate with their current size
52451      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
52452      */
52453     fitColumns : function(reserveScrollSpace){
52454         var cm = this.grid.colModel;
52455         var colCount = cm.getColumnCount();
52456         var cols = [];
52457         var width = 0;
52458         var i, w;
52459         for (i = 0; i < colCount; i++){
52460             if(!cm.isHidden(i) && !cm.isFixed(i)){
52461                 w = cm.getColumnWidth(i);
52462                 cols.push(i);
52463                 cols.push(w);
52464                 width += w;
52465             }
52466         }
52467         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
52468         if(reserveScrollSpace){
52469             avail -= 17;
52470         }
52471         var frac = (avail - cm.getTotalWidth())/width;
52472         while (cols.length){
52473             w = cols.pop();
52474             i = cols.pop();
52475             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
52476         }
52477         this.updateColumns();
52478         this.layout();
52479     },
52480
52481     onRowSelect : function(rowIndex){
52482         var row = this.getRowComposite(rowIndex);
52483         row.addClass("x-grid-row-selected");
52484     },
52485
52486     onRowDeselect : function(rowIndex){
52487         var row = this.getRowComposite(rowIndex);
52488         row.removeClass("x-grid-row-selected");
52489     },
52490
52491     onCellSelect : function(row, col){
52492         var cell = this.getCell(row, col);
52493         if(cell){
52494             Roo.fly(cell).addClass("x-grid-cell-selected");
52495         }
52496     },
52497
52498     onCellDeselect : function(row, col){
52499         var cell = this.getCell(row, col);
52500         if(cell){
52501             Roo.fly(cell).removeClass("x-grid-cell-selected");
52502         }
52503     },
52504
52505     updateHeaderSortState : function(){
52506         
52507         // sort state can be single { field: xxx, direction : yyy}
52508         // or   { xxx=>ASC , yyy : DESC ..... }
52509         
52510         var mstate = {};
52511         if (!this.ds.multiSort) { 
52512             var state = this.ds.getSortState();
52513             if(!state){
52514                 return;
52515             }
52516             mstate[state.field] = state.direction;
52517             // FIXME... - this is not used here.. but might be elsewhere..
52518             this.sortState = state;
52519             
52520         } else {
52521             mstate = this.ds.sortToggle;
52522         }
52523         //remove existing sort classes..
52524         
52525         var sc = this.sortClasses;
52526         var hds = this.el.select(this.headerSelector).removeClass(sc);
52527         
52528         for(var f in mstate) {
52529         
52530             var sortColumn = this.cm.findColumnIndex(f);
52531             
52532             if(sortColumn != -1){
52533                 var sortDir = mstate[f];        
52534                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
52535             }
52536         }
52537         
52538          
52539         
52540     },
52541
52542
52543     handleHeaderClick : function(g, index){
52544         if(this.headersDisabled){
52545             return;
52546         }
52547         var dm = g.dataSource, cm = g.colModel;
52548         if(!cm.isSortable(index)){
52549             return;
52550         }
52551         g.stopEditing();
52552         
52553         if (dm.multiSort) {
52554             // update the sortOrder
52555             var so = [];
52556             for(var i = 0; i < cm.config.length; i++ ) {
52557                 
52558                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
52559                     continue; // dont' bother, it's not in sort list or being set.
52560                 }
52561                 
52562                 so.push(cm.config[i].dataIndex);
52563             };
52564             dm.sortOrder = so;
52565         }
52566         
52567         
52568         dm.sort(cm.getDataIndex(index));
52569     },
52570
52571
52572     destroy : function(){
52573         if(this.colMenu){
52574             this.colMenu.removeAll();
52575             Roo.menu.MenuMgr.unregister(this.colMenu);
52576             this.colMenu.getEl().remove();
52577             delete this.colMenu;
52578         }
52579         if(this.hmenu){
52580             this.hmenu.removeAll();
52581             Roo.menu.MenuMgr.unregister(this.hmenu);
52582             this.hmenu.getEl().remove();
52583             delete this.hmenu;
52584         }
52585         if(this.grid.enableColumnMove){
52586             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
52587             if(dds){
52588                 for(var dd in dds){
52589                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
52590                         var elid = dds[dd].dragElId;
52591                         dds[dd].unreg();
52592                         Roo.get(elid).remove();
52593                     } else if(dds[dd].config.isTarget){
52594                         dds[dd].proxyTop.remove();
52595                         dds[dd].proxyBottom.remove();
52596                         dds[dd].unreg();
52597                     }
52598                     if(Roo.dd.DDM.locationCache[dd]){
52599                         delete Roo.dd.DDM.locationCache[dd];
52600                     }
52601                 }
52602                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
52603             }
52604         }
52605         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
52606         this.bind(null, null);
52607         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
52608     },
52609
52610     handleLockChange : function(){
52611         this.refresh(true);
52612     },
52613
52614     onDenyColumnLock : function(){
52615
52616     },
52617
52618     onDenyColumnHide : function(){
52619
52620     },
52621
52622     handleHdMenuClick : function(item){
52623         var index = this.hdCtxIndex;
52624         var cm = this.cm, ds = this.ds;
52625         switch(item.id){
52626             case "asc":
52627                 ds.sort(cm.getDataIndex(index), "ASC");
52628                 break;
52629             case "desc":
52630                 ds.sort(cm.getDataIndex(index), "DESC");
52631                 break;
52632             case "lock":
52633                 var lc = cm.getLockedCount();
52634                 if(cm.getColumnCount(true) <= lc+1){
52635                     this.onDenyColumnLock();
52636                     return;
52637                 }
52638                 if(lc != index){
52639                     cm.setLocked(index, true, true);
52640                     cm.moveColumn(index, lc);
52641                     this.grid.fireEvent("columnmove", index, lc);
52642                 }else{
52643                     cm.setLocked(index, true);
52644                 }
52645             break;
52646             case "unlock":
52647                 var lc = cm.getLockedCount();
52648                 if((lc-1) != index){
52649                     cm.setLocked(index, false, true);
52650                     cm.moveColumn(index, lc-1);
52651                     this.grid.fireEvent("columnmove", index, lc-1);
52652                 }else{
52653                     cm.setLocked(index, false);
52654                 }
52655             break;
52656             default:
52657                 index = cm.getIndexById(item.id.substr(4));
52658                 if(index != -1){
52659                     if(item.checked && cm.getColumnCount(true) <= 1){
52660                         this.onDenyColumnHide();
52661                         return false;
52662                     }
52663                     cm.setHidden(index, item.checked);
52664                 }
52665         }
52666         return true;
52667     },
52668
52669     beforeColMenuShow : function(){
52670         var cm = this.cm,  colCount = cm.getColumnCount();
52671         this.colMenu.removeAll();
52672         for(var i = 0; i < colCount; i++){
52673             this.colMenu.add(new Roo.menu.CheckItem({
52674                 id: "col-"+cm.getColumnId(i),
52675                 text: cm.getColumnHeader(i),
52676                 checked: !cm.isHidden(i),
52677                 hideOnClick:false
52678             }));
52679         }
52680     },
52681
52682     handleHdCtx : function(g, index, e){
52683         e.stopEvent();
52684         var hd = this.getHeaderCell(index);
52685         this.hdCtxIndex = index;
52686         var ms = this.hmenu.items, cm = this.cm;
52687         ms.get("asc").setDisabled(!cm.isSortable(index));
52688         ms.get("desc").setDisabled(!cm.isSortable(index));
52689         if(this.grid.enableColLock !== false){
52690             ms.get("lock").setDisabled(cm.isLocked(index));
52691             ms.get("unlock").setDisabled(!cm.isLocked(index));
52692         }
52693         this.hmenu.show(hd, "tl-bl");
52694     },
52695
52696     handleHdOver : function(e){
52697         var hd = this.findHeaderCell(e.getTarget());
52698         if(hd && !this.headersDisabled){
52699             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
52700                this.fly(hd).addClass("x-grid-hd-over");
52701             }
52702         }
52703     },
52704
52705     handleHdOut : function(e){
52706         var hd = this.findHeaderCell(e.getTarget());
52707         if(hd){
52708             this.fly(hd).removeClass("x-grid-hd-over");
52709         }
52710     },
52711
52712     handleSplitDblClick : function(e, t){
52713         var i = this.getCellIndex(t);
52714         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
52715             this.autoSizeColumn(i, true);
52716             this.layout();
52717         }
52718     },
52719
52720     render : function(){
52721
52722         var cm = this.cm;
52723         var colCount = cm.getColumnCount();
52724
52725         if(this.grid.monitorWindowResize === true){
52726             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52727         }
52728         var header = this.renderHeaders();
52729         var body = this.templates.body.apply({rows:""});
52730         var html = this.templates.master.apply({
52731             lockedBody: body,
52732             body: body,
52733             lockedHeader: header[0],
52734             header: header[1]
52735         });
52736
52737         //this.updateColumns();
52738
52739         this.grid.getGridEl().dom.innerHTML = html;
52740
52741         this.initElements();
52742         
52743         // a kludge to fix the random scolling effect in webkit
52744         this.el.on("scroll", function() {
52745             this.el.dom.scrollTop=0; // hopefully not recursive..
52746         },this);
52747
52748         this.scroller.on("scroll", this.handleScroll, this);
52749         this.lockedBody.on("mousewheel", this.handleWheel, this);
52750         this.mainBody.on("mousewheel", this.handleWheel, this);
52751
52752         this.mainHd.on("mouseover", this.handleHdOver, this);
52753         this.mainHd.on("mouseout", this.handleHdOut, this);
52754         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
52755                 {delegate: "."+this.splitClass});
52756
52757         this.lockedHd.on("mouseover", this.handleHdOver, this);
52758         this.lockedHd.on("mouseout", this.handleHdOut, this);
52759         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
52760                 {delegate: "."+this.splitClass});
52761
52762         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
52763             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
52764         }
52765
52766         this.updateSplitters();
52767
52768         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
52769             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
52770             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
52771         }
52772
52773         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
52774             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
52775             this.hmenu.add(
52776                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
52777                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
52778             );
52779             if(this.grid.enableColLock !== false){
52780                 this.hmenu.add('-',
52781                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
52782                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
52783                 );
52784             }
52785             if(this.grid.enableColumnHide !== false){
52786
52787                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
52788                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
52789                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
52790
52791                 this.hmenu.add('-',
52792                     {id:"columns", text: this.columnsText, menu: this.colMenu}
52793                 );
52794             }
52795             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
52796
52797             this.grid.on("headercontextmenu", this.handleHdCtx, this);
52798         }
52799
52800         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
52801             this.dd = new Roo.grid.GridDragZone(this.grid, {
52802                 ddGroup : this.grid.ddGroup || 'GridDD'
52803             });
52804             
52805         }
52806
52807         /*
52808         for(var i = 0; i < colCount; i++){
52809             if(cm.isHidden(i)){
52810                 this.hideColumn(i);
52811             }
52812             if(cm.config[i].align){
52813                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
52814                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
52815             }
52816         }*/
52817         
52818         this.updateHeaderSortState();
52819
52820         this.beforeInitialResize();
52821         this.layout(true);
52822
52823         // two part rendering gives faster view to the user
52824         this.renderPhase2.defer(1, this);
52825     },
52826
52827     renderPhase2 : function(){
52828         // render the rows now
52829         this.refresh();
52830         if(this.grid.autoSizeColumns){
52831             this.autoSizeColumns();
52832         }
52833     },
52834
52835     beforeInitialResize : function(){
52836
52837     },
52838
52839     onColumnSplitterMoved : function(i, w){
52840         this.userResized = true;
52841         var cm = this.grid.colModel;
52842         cm.setColumnWidth(i, w, true);
52843         var cid = cm.getColumnId(i);
52844         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
52845         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
52846         this.updateSplitters();
52847         this.layout();
52848         this.grid.fireEvent("columnresize", i, w);
52849     },
52850
52851     syncRowHeights : function(startIndex, endIndex){
52852         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
52853             startIndex = startIndex || 0;
52854             var mrows = this.getBodyTable().rows;
52855             var lrows = this.getLockedTable().rows;
52856             var len = mrows.length-1;
52857             endIndex = Math.min(endIndex || len, len);
52858             for(var i = startIndex; i <= endIndex; i++){
52859                 var m = mrows[i], l = lrows[i];
52860                 var h = Math.max(m.offsetHeight, l.offsetHeight);
52861                 m.style.height = l.style.height = h + "px";
52862             }
52863         }
52864     },
52865
52866     layout : function(initialRender, is2ndPass){
52867         var g = this.grid;
52868         var auto = g.autoHeight;
52869         var scrollOffset = 16;
52870         var c = g.getGridEl(), cm = this.cm,
52871                 expandCol = g.autoExpandColumn,
52872                 gv = this;
52873         //c.beginMeasure();
52874
52875         if(!c.dom.offsetWidth){ // display:none?
52876             if(initialRender){
52877                 this.lockedWrap.show();
52878                 this.mainWrap.show();
52879             }
52880             return;
52881         }
52882
52883         var hasLock = this.cm.isLocked(0);
52884
52885         var tbh = this.headerPanel.getHeight();
52886         var bbh = this.footerPanel.getHeight();
52887
52888         if(auto){
52889             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
52890             var newHeight = ch + c.getBorderWidth("tb");
52891             if(g.maxHeight){
52892                 newHeight = Math.min(g.maxHeight, newHeight);
52893             }
52894             c.setHeight(newHeight);
52895         }
52896
52897         if(g.autoWidth){
52898             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
52899         }
52900
52901         var s = this.scroller;
52902
52903         var csize = c.getSize(true);
52904
52905         this.el.setSize(csize.width, csize.height);
52906
52907         this.headerPanel.setWidth(csize.width);
52908         this.footerPanel.setWidth(csize.width);
52909
52910         var hdHeight = this.mainHd.getHeight();
52911         var vw = csize.width;
52912         var vh = csize.height - (tbh + bbh);
52913
52914         s.setSize(vw, vh);
52915
52916         var bt = this.getBodyTable();
52917         var ltWidth = hasLock ?
52918                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
52919
52920         var scrollHeight = bt.offsetHeight;
52921         var scrollWidth = ltWidth + bt.offsetWidth;
52922         var vscroll = false, hscroll = false;
52923
52924         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
52925
52926         var lw = this.lockedWrap, mw = this.mainWrap;
52927         var lb = this.lockedBody, mb = this.mainBody;
52928
52929         setTimeout(function(){
52930             var t = s.dom.offsetTop;
52931             var w = s.dom.clientWidth,
52932                 h = s.dom.clientHeight;
52933
52934             lw.setTop(t);
52935             lw.setSize(ltWidth, h);
52936
52937             mw.setLeftTop(ltWidth, t);
52938             mw.setSize(w-ltWidth, h);
52939
52940             lb.setHeight(h-hdHeight);
52941             mb.setHeight(h-hdHeight);
52942
52943             if(is2ndPass !== true && !gv.userResized && expandCol){
52944                 // high speed resize without full column calculation
52945                 
52946                 var ci = cm.getIndexById(expandCol);
52947                 if (ci < 0) {
52948                     ci = cm.findColumnIndex(expandCol);
52949                 }
52950                 ci = Math.max(0, ci); // make sure it's got at least the first col.
52951                 var expandId = cm.getColumnId(ci);
52952                 var  tw = cm.getTotalWidth(false);
52953                 var currentWidth = cm.getColumnWidth(ci);
52954                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
52955                 if(currentWidth != cw){
52956                     cm.setColumnWidth(ci, cw, true);
52957                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
52958                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
52959                     gv.updateSplitters();
52960                     gv.layout(false, true);
52961                 }
52962             }
52963
52964             if(initialRender){
52965                 lw.show();
52966                 mw.show();
52967             }
52968             //c.endMeasure();
52969         }, 10);
52970     },
52971
52972     onWindowResize : function(){
52973         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
52974             return;
52975         }
52976         this.layout();
52977     },
52978
52979     appendFooter : function(parentEl){
52980         return null;
52981     },
52982
52983     sortAscText : "Sort Ascending",
52984     sortDescText : "Sort Descending",
52985     lockText : "Lock Column",
52986     unlockText : "Unlock Column",
52987     columnsText : "Columns"
52988 });
52989
52990
52991 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
52992     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
52993     this.proxy.el.addClass('x-grid3-col-dd');
52994 };
52995
52996 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
52997     handleMouseDown : function(e){
52998
52999     },
53000
53001     callHandleMouseDown : function(e){
53002         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
53003     }
53004 });
53005 /*
53006  * Based on:
53007  * Ext JS Library 1.1.1
53008  * Copyright(c) 2006-2007, Ext JS, LLC.
53009  *
53010  * Originally Released Under LGPL - original licence link has changed is not relivant.
53011  *
53012  * Fork - LGPL
53013  * <script type="text/javascript">
53014  */
53015  
53016 // private
53017 // This is a support class used internally by the Grid components
53018 Roo.grid.SplitDragZone = function(grid, hd, hd2){
53019     this.grid = grid;
53020     this.view = grid.getView();
53021     this.proxy = this.view.resizeProxy;
53022     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
53023         "gridSplitters" + this.grid.getGridEl().id, {
53024         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
53025     });
53026     this.setHandleElId(Roo.id(hd));
53027     this.setOuterHandleElId(Roo.id(hd2));
53028     this.scroll = false;
53029 };
53030 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
53031     fly: Roo.Element.fly,
53032
53033     b4StartDrag : function(x, y){
53034         this.view.headersDisabled = true;
53035         this.proxy.setHeight(this.view.mainWrap.getHeight());
53036         var w = this.cm.getColumnWidth(this.cellIndex);
53037         var minw = Math.max(w-this.grid.minColumnWidth, 0);
53038         this.resetConstraints();
53039         this.setXConstraint(minw, 1000);
53040         this.setYConstraint(0, 0);
53041         this.minX = x - minw;
53042         this.maxX = x + 1000;
53043         this.startPos = x;
53044         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
53045     },
53046
53047
53048     handleMouseDown : function(e){
53049         ev = Roo.EventObject.setEvent(e);
53050         var t = this.fly(ev.getTarget());
53051         if(t.hasClass("x-grid-split")){
53052             this.cellIndex = this.view.getCellIndex(t.dom);
53053             this.split = t.dom;
53054             this.cm = this.grid.colModel;
53055             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
53056                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
53057             }
53058         }
53059     },
53060
53061     endDrag : function(e){
53062         this.view.headersDisabled = false;
53063         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
53064         var diff = endX - this.startPos;
53065         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
53066     },
53067
53068     autoOffset : function(){
53069         this.setDelta(0,0);
53070     }
53071 });/*
53072  * Based on:
53073  * Ext JS Library 1.1.1
53074  * Copyright(c) 2006-2007, Ext JS, LLC.
53075  *
53076  * Originally Released Under LGPL - original licence link has changed is not relivant.
53077  *
53078  * Fork - LGPL
53079  * <script type="text/javascript">
53080  */
53081  
53082 // private
53083 // This is a support class used internally by the Grid components
53084 Roo.grid.GridDragZone = function(grid, config){
53085     this.view = grid.getView();
53086     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
53087     if(this.view.lockedBody){
53088         this.setHandleElId(Roo.id(this.view.mainBody.dom));
53089         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
53090     }
53091     this.scroll = false;
53092     this.grid = grid;
53093     this.ddel = document.createElement('div');
53094     this.ddel.className = 'x-grid-dd-wrap';
53095 };
53096
53097 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
53098     ddGroup : "GridDD",
53099
53100     getDragData : function(e){
53101         var t = Roo.lib.Event.getTarget(e);
53102         var rowIndex = this.view.findRowIndex(t);
53103         var sm = this.grid.selModel;
53104             
53105         //Roo.log(rowIndex);
53106         
53107         if (sm.getSelectedCell) {
53108             // cell selection..
53109             if (!sm.getSelectedCell()) {
53110                 return false;
53111             }
53112             if (rowIndex != sm.getSelectedCell()[0]) {
53113                 return false;
53114             }
53115         
53116         }
53117         
53118         if(rowIndex !== false){
53119             
53120             // if editorgrid.. 
53121             
53122             
53123             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
53124                
53125             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
53126               //  
53127             //}
53128             if (e.hasModifier()){
53129                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
53130             }
53131             
53132             Roo.log("getDragData");
53133             
53134             return {
53135                 grid: this.grid,
53136                 ddel: this.ddel,
53137                 rowIndex: rowIndex,
53138                 selections:sm.getSelections ? sm.getSelections() : (
53139                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
53140                 )
53141             };
53142         }
53143         return false;
53144     },
53145
53146     onInitDrag : function(e){
53147         var data = this.dragData;
53148         this.ddel.innerHTML = this.grid.getDragDropText();
53149         this.proxy.update(this.ddel);
53150         // fire start drag?
53151     },
53152
53153     afterRepair : function(){
53154         this.dragging = false;
53155     },
53156
53157     getRepairXY : function(e, data){
53158         return false;
53159     },
53160
53161     onEndDrag : function(data, e){
53162         // fire end drag?
53163     },
53164
53165     onValidDrop : function(dd, e, id){
53166         // fire drag drop?
53167         this.hideProxy();
53168     },
53169
53170     beforeInvalidDrop : function(e, id){
53171
53172     }
53173 });/*
53174  * Based on:
53175  * Ext JS Library 1.1.1
53176  * Copyright(c) 2006-2007, Ext JS, LLC.
53177  *
53178  * Originally Released Under LGPL - original licence link has changed is not relivant.
53179  *
53180  * Fork - LGPL
53181  * <script type="text/javascript">
53182  */
53183  
53184
53185 /**
53186  * @class Roo.grid.ColumnModel
53187  * @extends Roo.util.Observable
53188  * This is the default implementation of a ColumnModel used by the Grid. It defines
53189  * the columns in the grid.
53190  * <br>Usage:<br>
53191  <pre><code>
53192  var colModel = new Roo.grid.ColumnModel([
53193         {header: "Ticker", width: 60, sortable: true, locked: true},
53194         {header: "Company Name", width: 150, sortable: true},
53195         {header: "Market Cap.", width: 100, sortable: true},
53196         {header: "$ Sales", width: 100, sortable: true, renderer: money},
53197         {header: "Employees", width: 100, sortable: true, resizable: false}
53198  ]);
53199  </code></pre>
53200  * <p>
53201  
53202  * The config options listed for this class are options which may appear in each
53203  * individual column definition.
53204  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
53205  * @constructor
53206  * @param {Object} config An Array of column config objects. See this class's
53207  * config objects for details.
53208 */
53209 Roo.grid.ColumnModel = function(config){
53210         /**
53211      * The config passed into the constructor
53212      */
53213     this.config = config;
53214     this.lookup = {};
53215
53216     // if no id, create one
53217     // if the column does not have a dataIndex mapping,
53218     // map it to the order it is in the config
53219     for(var i = 0, len = config.length; i < len; i++){
53220         var c = config[i];
53221         if(typeof c.dataIndex == "undefined"){
53222             c.dataIndex = i;
53223         }
53224         if(typeof c.renderer == "string"){
53225             c.renderer = Roo.util.Format[c.renderer];
53226         }
53227         if(typeof c.id == "undefined"){
53228             c.id = Roo.id();
53229         }
53230         if(c.editor && c.editor.xtype){
53231             c.editor  = Roo.factory(c.editor, Roo.grid);
53232         }
53233         if(c.editor && c.editor.isFormField){
53234             c.editor = new Roo.grid.GridEditor(c.editor);
53235         }
53236         this.lookup[c.id] = c;
53237     }
53238
53239     /**
53240      * The width of columns which have no width specified (defaults to 100)
53241      * @type Number
53242      */
53243     this.defaultWidth = 100;
53244
53245     /**
53246      * Default sortable of columns which have no sortable specified (defaults to false)
53247      * @type Boolean
53248      */
53249     this.defaultSortable = false;
53250
53251     this.addEvents({
53252         /**
53253              * @event widthchange
53254              * Fires when the width of a column changes.
53255              * @param {ColumnModel} this
53256              * @param {Number} columnIndex The column index
53257              * @param {Number} newWidth The new width
53258              */
53259             "widthchange": true,
53260         /**
53261              * @event headerchange
53262              * Fires when the text of a header changes.
53263              * @param {ColumnModel} this
53264              * @param {Number} columnIndex The column index
53265              * @param {Number} newText The new header text
53266              */
53267             "headerchange": true,
53268         /**
53269              * @event hiddenchange
53270              * Fires when a column is hidden or "unhidden".
53271              * @param {ColumnModel} this
53272              * @param {Number} columnIndex The column index
53273              * @param {Boolean} hidden true if hidden, false otherwise
53274              */
53275             "hiddenchange": true,
53276             /**
53277          * @event columnmoved
53278          * Fires when a column is moved.
53279          * @param {ColumnModel} this
53280          * @param {Number} oldIndex
53281          * @param {Number} newIndex
53282          */
53283         "columnmoved" : true,
53284         /**
53285          * @event columlockchange
53286          * Fires when a column's locked state is changed
53287          * @param {ColumnModel} this
53288          * @param {Number} colIndex
53289          * @param {Boolean} locked true if locked
53290          */
53291         "columnlockchange" : true
53292     });
53293     Roo.grid.ColumnModel.superclass.constructor.call(this);
53294 };
53295 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
53296     /**
53297      * @cfg {String} header The header text to display in the Grid view.
53298      */
53299     /**
53300      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
53301      * {@link Roo.data.Record} definition from which to draw the column's value. If not
53302      * specified, the column's index is used as an index into the Record's data Array.
53303      */
53304     /**
53305      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
53306      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
53307      */
53308     /**
53309      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
53310      * Defaults to the value of the {@link #defaultSortable} property.
53311      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
53312      */
53313     /**
53314      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
53315      */
53316     /**
53317      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
53318      */
53319     /**
53320      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
53321      */
53322     /**
53323      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
53324      */
53325     /**
53326      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
53327      * given the cell's data value. See {@link #setRenderer}. If not specified, the
53328      * default renderer uses the raw data value.
53329      */
53330        /**
53331      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
53332      */
53333     /**
53334      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
53335      */
53336
53337     /**
53338      * Returns the id of the column at the specified index.
53339      * @param {Number} index The column index
53340      * @return {String} the id
53341      */
53342     getColumnId : function(index){
53343         return this.config[index].id;
53344     },
53345
53346     /**
53347      * Returns the column for a specified id.
53348      * @param {String} id The column id
53349      * @return {Object} the column
53350      */
53351     getColumnById : function(id){
53352         return this.lookup[id];
53353     },
53354
53355     
53356     /**
53357      * Returns the column for a specified dataIndex.
53358      * @param {String} dataIndex The column dataIndex
53359      * @return {Object|Boolean} the column or false if not found
53360      */
53361     getColumnByDataIndex: function(dataIndex){
53362         var index = this.findColumnIndex(dataIndex);
53363         return index > -1 ? this.config[index] : false;
53364     },
53365     
53366     /**
53367      * Returns the index for a specified column id.
53368      * @param {String} id The column id
53369      * @return {Number} the index, or -1 if not found
53370      */
53371     getIndexById : function(id){
53372         for(var i = 0, len = this.config.length; i < len; i++){
53373             if(this.config[i].id == id){
53374                 return i;
53375             }
53376         }
53377         return -1;
53378     },
53379     
53380     /**
53381      * Returns the index for a specified column dataIndex.
53382      * @param {String} dataIndex The column dataIndex
53383      * @return {Number} the index, or -1 if not found
53384      */
53385     
53386     findColumnIndex : function(dataIndex){
53387         for(var i = 0, len = this.config.length; i < len; i++){
53388             if(this.config[i].dataIndex == dataIndex){
53389                 return i;
53390             }
53391         }
53392         return -1;
53393     },
53394     
53395     
53396     moveColumn : function(oldIndex, newIndex){
53397         var c = this.config[oldIndex];
53398         this.config.splice(oldIndex, 1);
53399         this.config.splice(newIndex, 0, c);
53400         this.dataMap = null;
53401         this.fireEvent("columnmoved", this, oldIndex, newIndex);
53402     },
53403
53404     isLocked : function(colIndex){
53405         return this.config[colIndex].locked === true;
53406     },
53407
53408     setLocked : function(colIndex, value, suppressEvent){
53409         if(this.isLocked(colIndex) == value){
53410             return;
53411         }
53412         this.config[colIndex].locked = value;
53413         if(!suppressEvent){
53414             this.fireEvent("columnlockchange", this, colIndex, value);
53415         }
53416     },
53417
53418     getTotalLockedWidth : function(){
53419         var totalWidth = 0;
53420         for(var i = 0; i < this.config.length; i++){
53421             if(this.isLocked(i) && !this.isHidden(i)){
53422                 this.totalWidth += this.getColumnWidth(i);
53423             }
53424         }
53425         return totalWidth;
53426     },
53427
53428     getLockedCount : function(){
53429         for(var i = 0, len = this.config.length; i < len; i++){
53430             if(!this.isLocked(i)){
53431                 return i;
53432             }
53433         }
53434     },
53435
53436     /**
53437      * Returns the number of columns.
53438      * @return {Number}
53439      */
53440     getColumnCount : function(visibleOnly){
53441         if(visibleOnly === true){
53442             var c = 0;
53443             for(var i = 0, len = this.config.length; i < len; i++){
53444                 if(!this.isHidden(i)){
53445                     c++;
53446                 }
53447             }
53448             return c;
53449         }
53450         return this.config.length;
53451     },
53452
53453     /**
53454      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
53455      * @param {Function} fn
53456      * @param {Object} scope (optional)
53457      * @return {Array} result
53458      */
53459     getColumnsBy : function(fn, scope){
53460         var r = [];
53461         for(var i = 0, len = this.config.length; i < len; i++){
53462             var c = this.config[i];
53463             if(fn.call(scope||this, c, i) === true){
53464                 r[r.length] = c;
53465             }
53466         }
53467         return r;
53468     },
53469
53470     /**
53471      * Returns true if the specified column is sortable.
53472      * @param {Number} col The column index
53473      * @return {Boolean}
53474      */
53475     isSortable : function(col){
53476         if(typeof this.config[col].sortable == "undefined"){
53477             return this.defaultSortable;
53478         }
53479         return this.config[col].sortable;
53480     },
53481
53482     /**
53483      * Returns the rendering (formatting) function defined for the column.
53484      * @param {Number} col The column index.
53485      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
53486      */
53487     getRenderer : function(col){
53488         if(!this.config[col].renderer){
53489             return Roo.grid.ColumnModel.defaultRenderer;
53490         }
53491         return this.config[col].renderer;
53492     },
53493
53494     /**
53495      * Sets the rendering (formatting) function for a column.
53496      * @param {Number} col The column index
53497      * @param {Function} fn The function to use to process the cell's raw data
53498      * to return HTML markup for the grid view. The render function is called with
53499      * the following parameters:<ul>
53500      * <li>Data value.</li>
53501      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
53502      * <li>css A CSS style string to apply to the table cell.</li>
53503      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
53504      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
53505      * <li>Row index</li>
53506      * <li>Column index</li>
53507      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
53508      */
53509     setRenderer : function(col, fn){
53510         this.config[col].renderer = fn;
53511     },
53512
53513     /**
53514      * Returns the width for the specified column.
53515      * @param {Number} col The column index
53516      * @return {Number}
53517      */
53518     getColumnWidth : function(col){
53519         return this.config[col].width * 1 || this.defaultWidth;
53520     },
53521
53522     /**
53523      * Sets the width for a column.
53524      * @param {Number} col The column index
53525      * @param {Number} width The new width
53526      */
53527     setColumnWidth : function(col, width, suppressEvent){
53528         this.config[col].width = width;
53529         this.totalWidth = null;
53530         if(!suppressEvent){
53531              this.fireEvent("widthchange", this, col, width);
53532         }
53533     },
53534
53535     /**
53536      * Returns the total width of all columns.
53537      * @param {Boolean} includeHidden True to include hidden column widths
53538      * @return {Number}
53539      */
53540     getTotalWidth : function(includeHidden){
53541         if(!this.totalWidth){
53542             this.totalWidth = 0;
53543             for(var i = 0, len = this.config.length; i < len; i++){
53544                 if(includeHidden || !this.isHidden(i)){
53545                     this.totalWidth += this.getColumnWidth(i);
53546                 }
53547             }
53548         }
53549         return this.totalWidth;
53550     },
53551
53552     /**
53553      * Returns the header for the specified column.
53554      * @param {Number} col The column index
53555      * @return {String}
53556      */
53557     getColumnHeader : function(col){
53558         return this.config[col].header;
53559     },
53560
53561     /**
53562      * Sets the header for a column.
53563      * @param {Number} col The column index
53564      * @param {String} header The new header
53565      */
53566     setColumnHeader : function(col, header){
53567         this.config[col].header = header;
53568         this.fireEvent("headerchange", this, col, header);
53569     },
53570
53571     /**
53572      * Returns the tooltip for the specified column.
53573      * @param {Number} col The column index
53574      * @return {String}
53575      */
53576     getColumnTooltip : function(col){
53577             return this.config[col].tooltip;
53578     },
53579     /**
53580      * Sets the tooltip for a column.
53581      * @param {Number} col The column index
53582      * @param {String} tooltip The new tooltip
53583      */
53584     setColumnTooltip : function(col, tooltip){
53585             this.config[col].tooltip = tooltip;
53586     },
53587
53588     /**
53589      * Returns the dataIndex for the specified column.
53590      * @param {Number} col The column index
53591      * @return {Number}
53592      */
53593     getDataIndex : function(col){
53594         return this.config[col].dataIndex;
53595     },
53596
53597     /**
53598      * Sets the dataIndex for a column.
53599      * @param {Number} col The column index
53600      * @param {Number} dataIndex The new dataIndex
53601      */
53602     setDataIndex : function(col, dataIndex){
53603         this.config[col].dataIndex = dataIndex;
53604     },
53605
53606     
53607     
53608     /**
53609      * Returns true if the cell is editable.
53610      * @param {Number} colIndex The column index
53611      * @param {Number} rowIndex The row index
53612      * @return {Boolean}
53613      */
53614     isCellEditable : function(colIndex, rowIndex){
53615         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
53616     },
53617
53618     /**
53619      * Returns the editor defined for the cell/column.
53620      * return false or null to disable editing.
53621      * @param {Number} colIndex The column index
53622      * @param {Number} rowIndex The row index
53623      * @return {Object}
53624      */
53625     getCellEditor : function(colIndex, rowIndex){
53626         return this.config[colIndex].editor;
53627     },
53628
53629     /**
53630      * Sets if a column is editable.
53631      * @param {Number} col The column index
53632      * @param {Boolean} editable True if the column is editable
53633      */
53634     setEditable : function(col, editable){
53635         this.config[col].editable = editable;
53636     },
53637
53638
53639     /**
53640      * Returns true if the column is hidden.
53641      * @param {Number} colIndex The column index
53642      * @return {Boolean}
53643      */
53644     isHidden : function(colIndex){
53645         return this.config[colIndex].hidden;
53646     },
53647
53648
53649     /**
53650      * Returns true if the column width cannot be changed
53651      */
53652     isFixed : function(colIndex){
53653         return this.config[colIndex].fixed;
53654     },
53655
53656     /**
53657      * Returns true if the column can be resized
53658      * @return {Boolean}
53659      */
53660     isResizable : function(colIndex){
53661         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
53662     },
53663     /**
53664      * Sets if a column is hidden.
53665      * @param {Number} colIndex The column index
53666      * @param {Boolean} hidden True if the column is hidden
53667      */
53668     setHidden : function(colIndex, hidden){
53669         this.config[colIndex].hidden = hidden;
53670         this.totalWidth = null;
53671         this.fireEvent("hiddenchange", this, colIndex, hidden);
53672     },
53673
53674     /**
53675      * Sets the editor for a column.
53676      * @param {Number} col The column index
53677      * @param {Object} editor The editor object
53678      */
53679     setEditor : function(col, editor){
53680         this.config[col].editor = editor;
53681     }
53682 });
53683
53684 Roo.grid.ColumnModel.defaultRenderer = function(value){
53685         if(typeof value == "string" && value.length < 1){
53686             return "&#160;";
53687         }
53688         return value;
53689 };
53690
53691 // Alias for backwards compatibility
53692 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
53693 /*
53694  * Based on:
53695  * Ext JS Library 1.1.1
53696  * Copyright(c) 2006-2007, Ext JS, LLC.
53697  *
53698  * Originally Released Under LGPL - original licence link has changed is not relivant.
53699  *
53700  * Fork - LGPL
53701  * <script type="text/javascript">
53702  */
53703
53704 /**
53705  * @class Roo.grid.AbstractSelectionModel
53706  * @extends Roo.util.Observable
53707  * Abstract base class for grid SelectionModels.  It provides the interface that should be
53708  * implemented by descendant classes.  This class should not be directly instantiated.
53709  * @constructor
53710  */
53711 Roo.grid.AbstractSelectionModel = function(){
53712     this.locked = false;
53713     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
53714 };
53715
53716 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
53717     /** @ignore Called by the grid automatically. Do not call directly. */
53718     init : function(grid){
53719         this.grid = grid;
53720         this.initEvents();
53721     },
53722
53723     /**
53724      * Locks the selections.
53725      */
53726     lock : function(){
53727         this.locked = true;
53728     },
53729
53730     /**
53731      * Unlocks the selections.
53732      */
53733     unlock : function(){
53734         this.locked = false;
53735     },
53736
53737     /**
53738      * Returns true if the selections are locked.
53739      * @return {Boolean}
53740      */
53741     isLocked : function(){
53742         return this.locked;
53743     }
53744 });/*
53745  * Based on:
53746  * Ext JS Library 1.1.1
53747  * Copyright(c) 2006-2007, Ext JS, LLC.
53748  *
53749  * Originally Released Under LGPL - original licence link has changed is not relivant.
53750  *
53751  * Fork - LGPL
53752  * <script type="text/javascript">
53753  */
53754 /**
53755  * @extends Roo.grid.AbstractSelectionModel
53756  * @class Roo.grid.RowSelectionModel
53757  * The default SelectionModel used by {@link Roo.grid.Grid}.
53758  * It supports multiple selections and keyboard selection/navigation. 
53759  * @constructor
53760  * @param {Object} config
53761  */
53762 Roo.grid.RowSelectionModel = function(config){
53763     Roo.apply(this, config);
53764     this.selections = new Roo.util.MixedCollection(false, function(o){
53765         return o.id;
53766     });
53767
53768     this.last = false;
53769     this.lastActive = false;
53770
53771     this.addEvents({
53772         /**
53773              * @event selectionchange
53774              * Fires when the selection changes
53775              * @param {SelectionModel} this
53776              */
53777             "selectionchange" : true,
53778         /**
53779              * @event afterselectionchange
53780              * Fires after the selection changes (eg. by key press or clicking)
53781              * @param {SelectionModel} this
53782              */
53783             "afterselectionchange" : true,
53784         /**
53785              * @event beforerowselect
53786              * Fires when a row is selected being selected, return false to cancel.
53787              * @param {SelectionModel} this
53788              * @param {Number} rowIndex The selected index
53789              * @param {Boolean} keepExisting False if other selections will be cleared
53790              */
53791             "beforerowselect" : true,
53792         /**
53793              * @event rowselect
53794              * Fires when a row is selected.
53795              * @param {SelectionModel} this
53796              * @param {Number} rowIndex The selected index
53797              * @param {Roo.data.Record} r The record
53798              */
53799             "rowselect" : true,
53800         /**
53801              * @event rowdeselect
53802              * Fires when a row is deselected.
53803              * @param {SelectionModel} this
53804              * @param {Number} rowIndex The selected index
53805              */
53806         "rowdeselect" : true
53807     });
53808     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
53809     this.locked = false;
53810 };
53811
53812 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
53813     /**
53814      * @cfg {Boolean} singleSelect
53815      * True to allow selection of only one row at a time (defaults to false)
53816      */
53817     singleSelect : false,
53818
53819     // private
53820     initEvents : function(){
53821
53822         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
53823             this.grid.on("mousedown", this.handleMouseDown, this);
53824         }else{ // allow click to work like normal
53825             this.grid.on("rowclick", this.handleDragableRowClick, this);
53826         }
53827
53828         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
53829             "up" : function(e){
53830                 if(!e.shiftKey){
53831                     this.selectPrevious(e.shiftKey);
53832                 }else if(this.last !== false && this.lastActive !== false){
53833                     var last = this.last;
53834                     this.selectRange(this.last,  this.lastActive-1);
53835                     this.grid.getView().focusRow(this.lastActive);
53836                     if(last !== false){
53837                         this.last = last;
53838                     }
53839                 }else{
53840                     this.selectFirstRow();
53841                 }
53842                 this.fireEvent("afterselectionchange", this);
53843             },
53844             "down" : function(e){
53845                 if(!e.shiftKey){
53846                     this.selectNext(e.shiftKey);
53847                 }else if(this.last !== false && this.lastActive !== false){
53848                     var last = this.last;
53849                     this.selectRange(this.last,  this.lastActive+1);
53850                     this.grid.getView().focusRow(this.lastActive);
53851                     if(last !== false){
53852                         this.last = last;
53853                     }
53854                 }else{
53855                     this.selectFirstRow();
53856                 }
53857                 this.fireEvent("afterselectionchange", this);
53858             },
53859             scope: this
53860         });
53861
53862         var view = this.grid.view;
53863         view.on("refresh", this.onRefresh, this);
53864         view.on("rowupdated", this.onRowUpdated, this);
53865         view.on("rowremoved", this.onRemove, this);
53866     },
53867
53868     // private
53869     onRefresh : function(){
53870         var ds = this.grid.dataSource, i, v = this.grid.view;
53871         var s = this.selections;
53872         s.each(function(r){
53873             if((i = ds.indexOfId(r.id)) != -1){
53874                 v.onRowSelect(i);
53875             }else{
53876                 s.remove(r);
53877             }
53878         });
53879     },
53880
53881     // private
53882     onRemove : function(v, index, r){
53883         this.selections.remove(r);
53884     },
53885
53886     // private
53887     onRowUpdated : function(v, index, r){
53888         if(this.isSelected(r)){
53889             v.onRowSelect(index);
53890         }
53891     },
53892
53893     /**
53894      * Select records.
53895      * @param {Array} records The records to select
53896      * @param {Boolean} keepExisting (optional) True to keep existing selections
53897      */
53898     selectRecords : function(records, keepExisting){
53899         if(!keepExisting){
53900             this.clearSelections();
53901         }
53902         var ds = this.grid.dataSource;
53903         for(var i = 0, len = records.length; i < len; i++){
53904             this.selectRow(ds.indexOf(records[i]), true);
53905         }
53906     },
53907
53908     /**
53909      * Gets the number of selected rows.
53910      * @return {Number}
53911      */
53912     getCount : function(){
53913         return this.selections.length;
53914     },
53915
53916     /**
53917      * Selects the first row in the grid.
53918      */
53919     selectFirstRow : function(){
53920         this.selectRow(0);
53921     },
53922
53923     /**
53924      * Select the last row.
53925      * @param {Boolean} keepExisting (optional) True to keep existing selections
53926      */
53927     selectLastRow : function(keepExisting){
53928         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
53929     },
53930
53931     /**
53932      * Selects the row immediately following the last selected row.
53933      * @param {Boolean} keepExisting (optional) True to keep existing selections
53934      */
53935     selectNext : function(keepExisting){
53936         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
53937             this.selectRow(this.last+1, keepExisting);
53938             this.grid.getView().focusRow(this.last);
53939         }
53940     },
53941
53942     /**
53943      * Selects the row that precedes the last selected row.
53944      * @param {Boolean} keepExisting (optional) True to keep existing selections
53945      */
53946     selectPrevious : function(keepExisting){
53947         if(this.last){
53948             this.selectRow(this.last-1, keepExisting);
53949             this.grid.getView().focusRow(this.last);
53950         }
53951     },
53952
53953     /**
53954      * Returns the selected records
53955      * @return {Array} Array of selected records
53956      */
53957     getSelections : function(){
53958         return [].concat(this.selections.items);
53959     },
53960
53961     /**
53962      * Returns the first selected record.
53963      * @return {Record}
53964      */
53965     getSelected : function(){
53966         return this.selections.itemAt(0);
53967     },
53968
53969
53970     /**
53971      * Clears all selections.
53972      */
53973     clearSelections : function(fast){
53974         if(this.locked) return;
53975         if(fast !== true){
53976             var ds = this.grid.dataSource;
53977             var s = this.selections;
53978             s.each(function(r){
53979                 this.deselectRow(ds.indexOfId(r.id));
53980             }, this);
53981             s.clear();
53982         }else{
53983             this.selections.clear();
53984         }
53985         this.last = false;
53986     },
53987
53988
53989     /**
53990      * Selects all rows.
53991      */
53992     selectAll : function(){
53993         if(this.locked) return;
53994         this.selections.clear();
53995         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
53996             this.selectRow(i, true);
53997         }
53998     },
53999
54000     /**
54001      * Returns True if there is a selection.
54002      * @return {Boolean}
54003      */
54004     hasSelection : function(){
54005         return this.selections.length > 0;
54006     },
54007
54008     /**
54009      * Returns True if the specified row is selected.
54010      * @param {Number/Record} record The record or index of the record to check
54011      * @return {Boolean}
54012      */
54013     isSelected : function(index){
54014         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
54015         return (r && this.selections.key(r.id) ? true : false);
54016     },
54017
54018     /**
54019      * Returns True if the specified record id is selected.
54020      * @param {String} id The id of record to check
54021      * @return {Boolean}
54022      */
54023     isIdSelected : function(id){
54024         return (this.selections.key(id) ? true : false);
54025     },
54026
54027     // private
54028     handleMouseDown : function(e, t){
54029         var view = this.grid.getView(), rowIndex;
54030         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
54031             return;
54032         };
54033         if(e.shiftKey && this.last !== false){
54034             var last = this.last;
54035             this.selectRange(last, rowIndex, e.ctrlKey);
54036             this.last = last; // reset the last
54037             view.focusRow(rowIndex);
54038         }else{
54039             var isSelected = this.isSelected(rowIndex);
54040             if(e.button !== 0 && isSelected){
54041                 view.focusRow(rowIndex);
54042             }else if(e.ctrlKey && isSelected){
54043                 this.deselectRow(rowIndex);
54044             }else if(!isSelected){
54045                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
54046                 view.focusRow(rowIndex);
54047             }
54048         }
54049         this.fireEvent("afterselectionchange", this);
54050     },
54051     // private
54052     handleDragableRowClick :  function(grid, rowIndex, e) 
54053     {
54054         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
54055             this.selectRow(rowIndex, false);
54056             grid.view.focusRow(rowIndex);
54057              this.fireEvent("afterselectionchange", this);
54058         }
54059     },
54060     
54061     /**
54062      * Selects multiple rows.
54063      * @param {Array} rows Array of the indexes of the row to select
54064      * @param {Boolean} keepExisting (optional) True to keep existing selections
54065      */
54066     selectRows : function(rows, keepExisting){
54067         if(!keepExisting){
54068             this.clearSelections();
54069         }
54070         for(var i = 0, len = rows.length; i < len; i++){
54071             this.selectRow(rows[i], true);
54072         }
54073     },
54074
54075     /**
54076      * Selects a range of rows. All rows in between startRow and endRow are also selected.
54077      * @param {Number} startRow The index of the first row in the range
54078      * @param {Number} endRow The index of the last row in the range
54079      * @param {Boolean} keepExisting (optional) True to retain existing selections
54080      */
54081     selectRange : function(startRow, endRow, keepExisting){
54082         if(this.locked) return;
54083         if(!keepExisting){
54084             this.clearSelections();
54085         }
54086         if(startRow <= endRow){
54087             for(var i = startRow; i <= endRow; i++){
54088                 this.selectRow(i, true);
54089             }
54090         }else{
54091             for(var i = startRow; i >= endRow; i--){
54092                 this.selectRow(i, true);
54093             }
54094         }
54095     },
54096
54097     /**
54098      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
54099      * @param {Number} startRow The index of the first row in the range
54100      * @param {Number} endRow The index of the last row in the range
54101      */
54102     deselectRange : function(startRow, endRow, preventViewNotify){
54103         if(this.locked) return;
54104         for(var i = startRow; i <= endRow; i++){
54105             this.deselectRow(i, preventViewNotify);
54106         }
54107     },
54108
54109     /**
54110      * Selects a row.
54111      * @param {Number} row The index of the row to select
54112      * @param {Boolean} keepExisting (optional) True to keep existing selections
54113      */
54114     selectRow : function(index, keepExisting, preventViewNotify){
54115         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
54116         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
54117             if(!keepExisting || this.singleSelect){
54118                 this.clearSelections();
54119             }
54120             var r = this.grid.dataSource.getAt(index);
54121             this.selections.add(r);
54122             this.last = this.lastActive = index;
54123             if(!preventViewNotify){
54124                 this.grid.getView().onRowSelect(index);
54125             }
54126             this.fireEvent("rowselect", this, index, r);
54127             this.fireEvent("selectionchange", this);
54128         }
54129     },
54130
54131     /**
54132      * Deselects a row.
54133      * @param {Number} row The index of the row to deselect
54134      */
54135     deselectRow : function(index, preventViewNotify){
54136         if(this.locked) return;
54137         if(this.last == index){
54138             this.last = false;
54139         }
54140         if(this.lastActive == index){
54141             this.lastActive = false;
54142         }
54143         var r = this.grid.dataSource.getAt(index);
54144         this.selections.remove(r);
54145         if(!preventViewNotify){
54146             this.grid.getView().onRowDeselect(index);
54147         }
54148         this.fireEvent("rowdeselect", this, index);
54149         this.fireEvent("selectionchange", this);
54150     },
54151
54152     // private
54153     restoreLast : function(){
54154         if(this._last){
54155             this.last = this._last;
54156         }
54157     },
54158
54159     // private
54160     acceptsNav : function(row, col, cm){
54161         return !cm.isHidden(col) && cm.isCellEditable(col, row);
54162     },
54163
54164     // private
54165     onEditorKey : function(field, e){
54166         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
54167         if(k == e.TAB){
54168             e.stopEvent();
54169             ed.completeEdit();
54170             if(e.shiftKey){
54171                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
54172             }else{
54173                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
54174             }
54175         }else if(k == e.ENTER && !e.ctrlKey){
54176             e.stopEvent();
54177             ed.completeEdit();
54178             if(e.shiftKey){
54179                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
54180             }else{
54181                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
54182             }
54183         }else if(k == e.ESC){
54184             ed.cancelEdit();
54185         }
54186         if(newCell){
54187             g.startEditing(newCell[0], newCell[1]);
54188         }
54189     }
54190 });/*
54191  * Based on:
54192  * Ext JS Library 1.1.1
54193  * Copyright(c) 2006-2007, Ext JS, LLC.
54194  *
54195  * Originally Released Under LGPL - original licence link has changed is not relivant.
54196  *
54197  * Fork - LGPL
54198  * <script type="text/javascript">
54199  */
54200 /**
54201  * @class Roo.grid.CellSelectionModel
54202  * @extends Roo.grid.AbstractSelectionModel
54203  * This class provides the basic implementation for cell selection in a grid.
54204  * @constructor
54205  * @param {Object} config The object containing the configuration of this model.
54206  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
54207  */
54208 Roo.grid.CellSelectionModel = function(config){
54209     Roo.apply(this, config);
54210
54211     this.selection = null;
54212
54213     this.addEvents({
54214         /**
54215              * @event beforerowselect
54216              * Fires before a cell is selected.
54217              * @param {SelectionModel} this
54218              * @param {Number} rowIndex The selected row index
54219              * @param {Number} colIndex The selected cell index
54220              */
54221             "beforecellselect" : true,
54222         /**
54223              * @event cellselect
54224              * Fires when a cell is selected.
54225              * @param {SelectionModel} this
54226              * @param {Number} rowIndex The selected row index
54227              * @param {Number} colIndex The selected cell index
54228              */
54229             "cellselect" : true,
54230         /**
54231              * @event selectionchange
54232              * Fires when the active selection changes.
54233              * @param {SelectionModel} this
54234              * @param {Object} selection null for no selection or an object (o) with two properties
54235                 <ul>
54236                 <li>o.record: the record object for the row the selection is in</li>
54237                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
54238                 </ul>
54239              */
54240             "selectionchange" : true,
54241         /**
54242              * @event tabend
54243              * Fires when the tab (or enter) was pressed on the last editable cell
54244              * You can use this to trigger add new row.
54245              * @param {SelectionModel} this
54246              */
54247             "tabend" : true,
54248          /**
54249              * @event beforeeditnext
54250              * Fires before the next editable sell is made active
54251              * You can use this to skip to another cell or fire the tabend
54252              *    if you set cell to false
54253              * @param {Object} eventdata object : { cell : [ row, col ] } 
54254              */
54255             "beforeeditnext" : true
54256     });
54257     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
54258 };
54259
54260 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
54261     
54262     enter_is_tab: false,
54263
54264     /** @ignore */
54265     initEvents : function(){
54266         this.grid.on("mousedown", this.handleMouseDown, this);
54267         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
54268         var view = this.grid.view;
54269         view.on("refresh", this.onViewChange, this);
54270         view.on("rowupdated", this.onRowUpdated, this);
54271         view.on("beforerowremoved", this.clearSelections, this);
54272         view.on("beforerowsinserted", this.clearSelections, this);
54273         if(this.grid.isEditor){
54274             this.grid.on("beforeedit", this.beforeEdit,  this);
54275         }
54276     },
54277
54278         //private
54279     beforeEdit : function(e){
54280         this.select(e.row, e.column, false, true, e.record);
54281     },
54282
54283         //private
54284     onRowUpdated : function(v, index, r){
54285         if(this.selection && this.selection.record == r){
54286             v.onCellSelect(index, this.selection.cell[1]);
54287         }
54288     },
54289
54290         //private
54291     onViewChange : function(){
54292         this.clearSelections(true);
54293     },
54294
54295         /**
54296          * Returns the currently selected cell,.
54297          * @return {Array} The selected cell (row, column) or null if none selected.
54298          */
54299     getSelectedCell : function(){
54300         return this.selection ? this.selection.cell : null;
54301     },
54302
54303     /**
54304      * Clears all selections.
54305      * @param {Boolean} true to prevent the gridview from being notified about the change.
54306      */
54307     clearSelections : function(preventNotify){
54308         var s = this.selection;
54309         if(s){
54310             if(preventNotify !== true){
54311                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
54312             }
54313             this.selection = null;
54314             this.fireEvent("selectionchange", this, null);
54315         }
54316     },
54317
54318     /**
54319      * Returns true if there is a selection.
54320      * @return {Boolean}
54321      */
54322     hasSelection : function(){
54323         return this.selection ? true : false;
54324     },
54325
54326     /** @ignore */
54327     handleMouseDown : function(e, t){
54328         var v = this.grid.getView();
54329         if(this.isLocked()){
54330             return;
54331         };
54332         var row = v.findRowIndex(t);
54333         var cell = v.findCellIndex(t);
54334         if(row !== false && cell !== false){
54335             this.select(row, cell);
54336         }
54337     },
54338
54339     /**
54340      * Selects a cell.
54341      * @param {Number} rowIndex
54342      * @param {Number} collIndex
54343      */
54344     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
54345         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
54346             this.clearSelections();
54347             r = r || this.grid.dataSource.getAt(rowIndex);
54348             this.selection = {
54349                 record : r,
54350                 cell : [rowIndex, colIndex]
54351             };
54352             if(!preventViewNotify){
54353                 var v = this.grid.getView();
54354                 v.onCellSelect(rowIndex, colIndex);
54355                 if(preventFocus !== true){
54356                     v.focusCell(rowIndex, colIndex);
54357                 }
54358             }
54359             this.fireEvent("cellselect", this, rowIndex, colIndex);
54360             this.fireEvent("selectionchange", this, this.selection);
54361         }
54362     },
54363
54364         //private
54365     isSelectable : function(rowIndex, colIndex, cm){
54366         return !cm.isHidden(colIndex);
54367     },
54368
54369     /** @ignore */
54370     handleKeyDown : function(e){
54371         //Roo.log('Cell Sel Model handleKeyDown');
54372         if(!e.isNavKeyPress()){
54373             return;
54374         }
54375         var g = this.grid, s = this.selection;
54376         if(!s){
54377             e.stopEvent();
54378             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
54379             if(cell){
54380                 this.select(cell[0], cell[1]);
54381             }
54382             return;
54383         }
54384         var sm = this;
54385         var walk = function(row, col, step){
54386             return g.walkCells(row, col, step, sm.isSelectable,  sm);
54387         };
54388         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
54389         var newCell;
54390
54391       
54392
54393         switch(k){
54394             case e.TAB:
54395                 // handled by onEditorKey
54396                 if (g.isEditor && g.editing) {
54397                     return;
54398                 }
54399                 if(e.shiftKey) {
54400                     newCell = walk(r, c-1, -1);
54401                 } else {
54402                     newCell = walk(r, c+1, 1);
54403                 }
54404                 break;
54405             
54406             case e.DOWN:
54407                newCell = walk(r+1, c, 1);
54408                 break;
54409             
54410             case e.UP:
54411                 newCell = walk(r-1, c, -1);
54412                 break;
54413             
54414             case e.RIGHT:
54415                 newCell = walk(r, c+1, 1);
54416                 break;
54417             
54418             case e.LEFT:
54419                 newCell = walk(r, c-1, -1);
54420                 break;
54421             
54422             case e.ENTER:
54423                 
54424                 if(g.isEditor && !g.editing){
54425                    g.startEditing(r, c);
54426                    e.stopEvent();
54427                    return;
54428                 }
54429                 
54430                 
54431              break;
54432         };
54433         if(newCell){
54434             this.select(newCell[0], newCell[1]);
54435             e.stopEvent();
54436             
54437         }
54438     },
54439
54440     acceptsNav : function(row, col, cm){
54441         return !cm.isHidden(col) && cm.isCellEditable(col, row);
54442     },
54443     /**
54444      * Selects a cell.
54445      * @param {Number} field (not used) - as it's normally used as a listener
54446      * @param {Number} e - event - fake it by using
54447      *
54448      * var e = Roo.EventObjectImpl.prototype;
54449      * e.keyCode = e.TAB
54450      *
54451      * 
54452      */
54453     onEditorKey : function(field, e){
54454         
54455         var k = e.getKey(),
54456             newCell,
54457             g = this.grid,
54458             ed = g.activeEditor,
54459             forward = false;
54460         ///Roo.log('onEditorKey' + k);
54461         
54462         
54463         if (this.enter_is_tab && k == e.ENTER) {
54464             k = e.TAB;
54465         }
54466         
54467         if(k == e.TAB){
54468             if(e.shiftKey){
54469                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
54470             }else{
54471                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
54472                 forward = true;
54473             }
54474             
54475             e.stopEvent();
54476             
54477         } else if(k == e.ENTER &&  !e.ctrlKey){
54478             ed.completeEdit();
54479             e.stopEvent();
54480             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
54481         
54482                 } else if(k == e.ESC){
54483             ed.cancelEdit();
54484         }
54485                 
54486         if (newCell) {
54487             var ecall = { cell : newCell, forward : forward };
54488             this.fireEvent('beforeeditnext', ecall );
54489             newCell = ecall.cell;
54490                         forward = ecall.forward;
54491         }
54492                 
54493         if(newCell){
54494             //Roo.log('next cell after edit');
54495             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
54496         } else if (forward) {
54497             // tabbed past last
54498             this.fireEvent.defer(100, this, ['tabend',this]);
54499         }
54500     }
54501 });/*
54502  * Based on:
54503  * Ext JS Library 1.1.1
54504  * Copyright(c) 2006-2007, Ext JS, LLC.
54505  *
54506  * Originally Released Under LGPL - original licence link has changed is not relivant.
54507  *
54508  * Fork - LGPL
54509  * <script type="text/javascript">
54510  */
54511  
54512 /**
54513  * @class Roo.grid.EditorGrid
54514  * @extends Roo.grid.Grid
54515  * Class for creating and editable grid.
54516  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
54517  * The container MUST have some type of size defined for the grid to fill. The container will be 
54518  * automatically set to position relative if it isn't already.
54519  * @param {Object} dataSource The data model to bind to
54520  * @param {Object} colModel The column model with info about this grid's columns
54521  */
54522 Roo.grid.EditorGrid = function(container, config){
54523     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
54524     this.getGridEl().addClass("xedit-grid");
54525
54526     if(!this.selModel){
54527         this.selModel = new Roo.grid.CellSelectionModel();
54528     }
54529
54530     this.activeEditor = null;
54531
54532         this.addEvents({
54533             /**
54534              * @event beforeedit
54535              * Fires before cell editing is triggered. The edit event object has the following properties <br />
54536              * <ul style="padding:5px;padding-left:16px;">
54537              * <li>grid - This grid</li>
54538              * <li>record - The record being edited</li>
54539              * <li>field - The field name being edited</li>
54540              * <li>value - The value for the field being edited.</li>
54541              * <li>row - The grid row index</li>
54542              * <li>column - The grid column index</li>
54543              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
54544              * </ul>
54545              * @param {Object} e An edit event (see above for description)
54546              */
54547             "beforeedit" : true,
54548             /**
54549              * @event afteredit
54550              * Fires after a cell is edited. <br />
54551              * <ul style="padding:5px;padding-left:16px;">
54552              * <li>grid - This grid</li>
54553              * <li>record - The record being edited</li>
54554              * <li>field - The field name being edited</li>
54555              * <li>value - The value being set</li>
54556              * <li>originalValue - The original value for the field, before the edit.</li>
54557              * <li>row - The grid row index</li>
54558              * <li>column - The grid column index</li>
54559              * </ul>
54560              * @param {Object} e An edit event (see above for description)
54561              */
54562             "afteredit" : true,
54563             /**
54564              * @event validateedit
54565              * Fires after a cell is edited, but before the value is set in the record. 
54566          * You can use this to modify the value being set in the field, Return false
54567              * to cancel the change. The edit event object has the following properties <br />
54568              * <ul style="padding:5px;padding-left:16px;">
54569          * <li>editor - This editor</li>
54570              * <li>grid - This grid</li>
54571              * <li>record - The record being edited</li>
54572              * <li>field - The field name being edited</li>
54573              * <li>value - The value being set</li>
54574              * <li>originalValue - The original value for the field, before the edit.</li>
54575              * <li>row - The grid row index</li>
54576              * <li>column - The grid column index</li>
54577              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
54578              * </ul>
54579              * @param {Object} e An edit event (see above for description)
54580              */
54581             "validateedit" : true
54582         });
54583     this.on("bodyscroll", this.stopEditing,  this);
54584     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
54585 };
54586
54587 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
54588     /**
54589      * @cfg {Number} clicksToEdit
54590      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
54591      */
54592     clicksToEdit: 2,
54593
54594     // private
54595     isEditor : true,
54596     // private
54597     trackMouseOver: false, // causes very odd FF errors
54598
54599     onCellDblClick : function(g, row, col){
54600         this.startEditing(row, col);
54601     },
54602
54603     onEditComplete : function(ed, value, startValue){
54604         this.editing = false;
54605         this.activeEditor = null;
54606         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
54607         var r = ed.record;
54608         var field = this.colModel.getDataIndex(ed.col);
54609         var e = {
54610             grid: this,
54611             record: r,
54612             field: field,
54613             originalValue: startValue,
54614             value: value,
54615             row: ed.row,
54616             column: ed.col,
54617             cancel:false,
54618             editor: ed
54619         };
54620         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
54621         cell.show();
54622           
54623         if(String(value) !== String(startValue)){
54624             
54625             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
54626                 r.set(field, e.value);
54627                 // if we are dealing with a combo box..
54628                 // then we also set the 'name' colum to be the displayField
54629                 if (ed.field.displayField && ed.field.name) {
54630                     r.set(ed.field.name, ed.field.el.dom.value);
54631                 }
54632                 
54633                 delete e.cancel; //?? why!!!
54634                 this.fireEvent("afteredit", e);
54635             }
54636         } else {
54637             this.fireEvent("afteredit", e); // always fire it!
54638         }
54639         this.view.focusCell(ed.row, ed.col);
54640     },
54641
54642     /**
54643      * Starts editing the specified for the specified row/column
54644      * @param {Number} rowIndex
54645      * @param {Number} colIndex
54646      */
54647     startEditing : function(row, col){
54648         this.stopEditing();
54649         if(this.colModel.isCellEditable(col, row)){
54650             this.view.ensureVisible(row, col, true);
54651           
54652             var r = this.dataSource.getAt(row);
54653             var field = this.colModel.getDataIndex(col);
54654             var cell = Roo.get(this.view.getCell(row,col));
54655             var e = {
54656                 grid: this,
54657                 record: r,
54658                 field: field,
54659                 value: r.data[field],
54660                 row: row,
54661                 column: col,
54662                 cancel:false 
54663             };
54664             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
54665                 this.editing = true;
54666                 var ed = this.colModel.getCellEditor(col, row);
54667                 
54668                 if (!ed) {
54669                     return;
54670                 }
54671                 if(!ed.rendered){
54672                     ed.render(ed.parentEl || document.body);
54673                 }
54674                 ed.field.reset();
54675                
54676                 cell.hide();
54677                 
54678                 (function(){ // complex but required for focus issues in safari, ie and opera
54679                     ed.row = row;
54680                     ed.col = col;
54681                     ed.record = r;
54682                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
54683                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
54684                     this.activeEditor = ed;
54685                     var v = r.data[field];
54686                     ed.startEdit(this.view.getCell(row, col), v);
54687                     // combo's with 'displayField and name set
54688                     if (ed.field.displayField && ed.field.name) {
54689                         ed.field.el.dom.value = r.data[ed.field.name];
54690                     }
54691                     
54692                     
54693                 }).defer(50, this);
54694             }
54695         }
54696     },
54697         
54698     /**
54699      * Stops any active editing
54700      */
54701     stopEditing : function(){
54702         if(this.activeEditor){
54703             this.activeEditor.completeEdit();
54704         }
54705         this.activeEditor = null;
54706     },
54707         
54708          /**
54709      * Called to get grid's drag proxy text, by default returns this.ddText.
54710      * @return {String}
54711      */
54712     getDragDropText : function(){
54713         var count = this.selModel.getSelectedCell() ? 1 : 0;
54714         return String.format(this.ddText, count, count == 1 ? '' : 's');
54715     }
54716         
54717 });/*
54718  * Based on:
54719  * Ext JS Library 1.1.1
54720  * Copyright(c) 2006-2007, Ext JS, LLC.
54721  *
54722  * Originally Released Under LGPL - original licence link has changed is not relivant.
54723  *
54724  * Fork - LGPL
54725  * <script type="text/javascript">
54726  */
54727
54728 // private - not really -- you end up using it !
54729 // This is a support class used internally by the Grid components
54730
54731 /**
54732  * @class Roo.grid.GridEditor
54733  * @extends Roo.Editor
54734  * Class for creating and editable grid elements.
54735  * @param {Object} config any settings (must include field)
54736  */
54737 Roo.grid.GridEditor = function(field, config){
54738     if (!config && field.field) {
54739         config = field;
54740         field = Roo.factory(config.field, Roo.form);
54741     }
54742     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
54743     field.monitorTab = false;
54744 };
54745
54746 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
54747     
54748     /**
54749      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
54750      */
54751     
54752     alignment: "tl-tl",
54753     autoSize: "width",
54754     hideEl : false,
54755     cls: "x-small-editor x-grid-editor",
54756     shim:false,
54757     shadow:"frame"
54758 });/*
54759  * Based on:
54760  * Ext JS Library 1.1.1
54761  * Copyright(c) 2006-2007, Ext JS, LLC.
54762  *
54763  * Originally Released Under LGPL - original licence link has changed is not relivant.
54764  *
54765  * Fork - LGPL
54766  * <script type="text/javascript">
54767  */
54768   
54769
54770   
54771 Roo.grid.PropertyRecord = Roo.data.Record.create([
54772     {name:'name',type:'string'},  'value'
54773 ]);
54774
54775
54776 Roo.grid.PropertyStore = function(grid, source){
54777     this.grid = grid;
54778     this.store = new Roo.data.Store({
54779         recordType : Roo.grid.PropertyRecord
54780     });
54781     this.store.on('update', this.onUpdate,  this);
54782     if(source){
54783         this.setSource(source);
54784     }
54785     Roo.grid.PropertyStore.superclass.constructor.call(this);
54786 };
54787
54788
54789
54790 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
54791     setSource : function(o){
54792         this.source = o;
54793         this.store.removeAll();
54794         var data = [];
54795         for(var k in o){
54796             if(this.isEditableValue(o[k])){
54797                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
54798             }
54799         }
54800         this.store.loadRecords({records: data}, {}, true);
54801     },
54802
54803     onUpdate : function(ds, record, type){
54804         if(type == Roo.data.Record.EDIT){
54805             var v = record.data['value'];
54806             var oldValue = record.modified['value'];
54807             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
54808                 this.source[record.id] = v;
54809                 record.commit();
54810                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
54811             }else{
54812                 record.reject();
54813             }
54814         }
54815     },
54816
54817     getProperty : function(row){
54818        return this.store.getAt(row);
54819     },
54820
54821     isEditableValue: function(val){
54822         if(val && val instanceof Date){
54823             return true;
54824         }else if(typeof val == 'object' || typeof val == 'function'){
54825             return false;
54826         }
54827         return true;
54828     },
54829
54830     setValue : function(prop, value){
54831         this.source[prop] = value;
54832         this.store.getById(prop).set('value', value);
54833     },
54834
54835     getSource : function(){
54836         return this.source;
54837     }
54838 });
54839
54840 Roo.grid.PropertyColumnModel = function(grid, store){
54841     this.grid = grid;
54842     var g = Roo.grid;
54843     g.PropertyColumnModel.superclass.constructor.call(this, [
54844         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
54845         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
54846     ]);
54847     this.store = store;
54848     this.bselect = Roo.DomHelper.append(document.body, {
54849         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
54850             {tag: 'option', value: 'true', html: 'true'},
54851             {tag: 'option', value: 'false', html: 'false'}
54852         ]
54853     });
54854     Roo.id(this.bselect);
54855     var f = Roo.form;
54856     this.editors = {
54857         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
54858         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
54859         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
54860         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
54861         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
54862     };
54863     this.renderCellDelegate = this.renderCell.createDelegate(this);
54864     this.renderPropDelegate = this.renderProp.createDelegate(this);
54865 };
54866
54867 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
54868     
54869     
54870     nameText : 'Name',
54871     valueText : 'Value',
54872     
54873     dateFormat : 'm/j/Y',
54874     
54875     
54876     renderDate : function(dateVal){
54877         return dateVal.dateFormat(this.dateFormat);
54878     },
54879
54880     renderBool : function(bVal){
54881         return bVal ? 'true' : 'false';
54882     },
54883
54884     isCellEditable : function(colIndex, rowIndex){
54885         return colIndex == 1;
54886     },
54887
54888     getRenderer : function(col){
54889         return col == 1 ?
54890             this.renderCellDelegate : this.renderPropDelegate;
54891     },
54892
54893     renderProp : function(v){
54894         return this.getPropertyName(v);
54895     },
54896
54897     renderCell : function(val){
54898         var rv = val;
54899         if(val instanceof Date){
54900             rv = this.renderDate(val);
54901         }else if(typeof val == 'boolean'){
54902             rv = this.renderBool(val);
54903         }
54904         return Roo.util.Format.htmlEncode(rv);
54905     },
54906
54907     getPropertyName : function(name){
54908         var pn = this.grid.propertyNames;
54909         return pn && pn[name] ? pn[name] : name;
54910     },
54911
54912     getCellEditor : function(colIndex, rowIndex){
54913         var p = this.store.getProperty(rowIndex);
54914         var n = p.data['name'], val = p.data['value'];
54915         
54916         if(typeof(this.grid.customEditors[n]) == 'string'){
54917             return this.editors[this.grid.customEditors[n]];
54918         }
54919         if(typeof(this.grid.customEditors[n]) != 'undefined'){
54920             return this.grid.customEditors[n];
54921         }
54922         if(val instanceof Date){
54923             return this.editors['date'];
54924         }else if(typeof val == 'number'){
54925             return this.editors['number'];
54926         }else if(typeof val == 'boolean'){
54927             return this.editors['boolean'];
54928         }else{
54929             return this.editors['string'];
54930         }
54931     }
54932 });
54933
54934 /**
54935  * @class Roo.grid.PropertyGrid
54936  * @extends Roo.grid.EditorGrid
54937  * This class represents the  interface of a component based property grid control.
54938  * <br><br>Usage:<pre><code>
54939  var grid = new Roo.grid.PropertyGrid("my-container-id", {
54940       
54941  });
54942  // set any options
54943  grid.render();
54944  * </code></pre>
54945   
54946  * @constructor
54947  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54948  * The container MUST have some type of size defined for the grid to fill. The container will be
54949  * automatically set to position relative if it isn't already.
54950  * @param {Object} config A config object that sets properties on this grid.
54951  */
54952 Roo.grid.PropertyGrid = function(container, config){
54953     config = config || {};
54954     var store = new Roo.grid.PropertyStore(this);
54955     this.store = store;
54956     var cm = new Roo.grid.PropertyColumnModel(this, store);
54957     store.store.sort('name', 'ASC');
54958     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
54959         ds: store.store,
54960         cm: cm,
54961         enableColLock:false,
54962         enableColumnMove:false,
54963         stripeRows:false,
54964         trackMouseOver: false,
54965         clicksToEdit:1
54966     }, config));
54967     this.getGridEl().addClass('x-props-grid');
54968     this.lastEditRow = null;
54969     this.on('columnresize', this.onColumnResize, this);
54970     this.addEvents({
54971          /**
54972              * @event beforepropertychange
54973              * Fires before a property changes (return false to stop?)
54974              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
54975              * @param {String} id Record Id
54976              * @param {String} newval New Value
54977          * @param {String} oldval Old Value
54978              */
54979         "beforepropertychange": true,
54980         /**
54981              * @event propertychange
54982              * Fires after a property changes
54983              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
54984              * @param {String} id Record Id
54985              * @param {String} newval New Value
54986          * @param {String} oldval Old Value
54987              */
54988         "propertychange": true
54989     });
54990     this.customEditors = this.customEditors || {};
54991 };
54992 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
54993     
54994      /**
54995      * @cfg {Object} customEditors map of colnames=> custom editors.
54996      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
54997      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
54998      * false disables editing of the field.
54999          */
55000     
55001       /**
55002      * @cfg {Object} propertyNames map of property Names to their displayed value
55003          */
55004     
55005     render : function(){
55006         Roo.grid.PropertyGrid.superclass.render.call(this);
55007         this.autoSize.defer(100, this);
55008     },
55009
55010     autoSize : function(){
55011         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
55012         if(this.view){
55013             this.view.fitColumns();
55014         }
55015     },
55016
55017     onColumnResize : function(){
55018         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
55019         this.autoSize();
55020     },
55021     /**
55022      * Sets the data for the Grid
55023      * accepts a Key => Value object of all the elements avaiable.
55024      * @param {Object} data  to appear in grid.
55025      */
55026     setSource : function(source){
55027         this.store.setSource(source);
55028         //this.autoSize();
55029     },
55030     /**
55031      * Gets all the data from the grid.
55032      * @return {Object} data  data stored in grid
55033      */
55034     getSource : function(){
55035         return this.store.getSource();
55036     }
55037 });/*
55038  * Based on:
55039  * Ext JS Library 1.1.1
55040  * Copyright(c) 2006-2007, Ext JS, LLC.
55041  *
55042  * Originally Released Under LGPL - original licence link has changed is not relivant.
55043  *
55044  * Fork - LGPL
55045  * <script type="text/javascript">
55046  */
55047  
55048 /**
55049  * @class Roo.LoadMask
55050  * A simple utility class for generically masking elements while loading data.  If the element being masked has
55051  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
55052  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
55053  * element's UpdateManager load indicator and will be destroyed after the initial load.
55054  * @constructor
55055  * Create a new LoadMask
55056  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
55057  * @param {Object} config The config object
55058  */
55059 Roo.LoadMask = function(el, config){
55060     this.el = Roo.get(el);
55061     Roo.apply(this, config);
55062     if(this.store){
55063         this.store.on('beforeload', this.onBeforeLoad, this);
55064         this.store.on('load', this.onLoad, this);
55065         this.store.on('loadexception', this.onLoadException, this);
55066         this.removeMask = false;
55067     }else{
55068         var um = this.el.getUpdateManager();
55069         um.showLoadIndicator = false; // disable the default indicator
55070         um.on('beforeupdate', this.onBeforeLoad, this);
55071         um.on('update', this.onLoad, this);
55072         um.on('failure', this.onLoad, this);
55073         this.removeMask = true;
55074     }
55075 };
55076
55077 Roo.LoadMask.prototype = {
55078     /**
55079      * @cfg {Boolean} removeMask
55080      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
55081      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
55082      */
55083     /**
55084      * @cfg {String} msg
55085      * The text to display in a centered loading message box (defaults to 'Loading...')
55086      */
55087     msg : 'Loading...',
55088     /**
55089      * @cfg {String} msgCls
55090      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
55091      */
55092     msgCls : 'x-mask-loading',
55093
55094     /**
55095      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
55096      * @type Boolean
55097      */
55098     disabled: false,
55099
55100     /**
55101      * Disables the mask to prevent it from being displayed
55102      */
55103     disable : function(){
55104        this.disabled = true;
55105     },
55106
55107     /**
55108      * Enables the mask so that it can be displayed
55109      */
55110     enable : function(){
55111         this.disabled = false;
55112     },
55113     
55114     onLoadException : function()
55115     {
55116         Roo.log(arguments);
55117         
55118         if (typeof(arguments[3]) != 'undefined') {
55119             Roo.MessageBox.alert("Error loading",arguments[3]);
55120         } 
55121         /*
55122         try {
55123             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
55124                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
55125             }   
55126         } catch(e) {
55127             
55128         }
55129         */
55130     
55131         
55132         
55133         this.el.unmask(this.removeMask);
55134     },
55135     // private
55136     onLoad : function()
55137     {
55138         this.el.unmask(this.removeMask);
55139     },
55140
55141     // private
55142     onBeforeLoad : function(){
55143         if(!this.disabled){
55144             this.el.mask(this.msg, this.msgCls);
55145         }
55146     },
55147
55148     // private
55149     destroy : function(){
55150         if(this.store){
55151             this.store.un('beforeload', this.onBeforeLoad, this);
55152             this.store.un('load', this.onLoad, this);
55153             this.store.un('loadexception', this.onLoadException, this);
55154         }else{
55155             var um = this.el.getUpdateManager();
55156             um.un('beforeupdate', this.onBeforeLoad, this);
55157             um.un('update', this.onLoad, this);
55158             um.un('failure', this.onLoad, this);
55159         }
55160     }
55161 };/*
55162  * Based on:
55163  * Ext JS Library 1.1.1
55164  * Copyright(c) 2006-2007, Ext JS, LLC.
55165  *
55166  * Originally Released Under LGPL - original licence link has changed is not relivant.
55167  *
55168  * Fork - LGPL
55169  * <script type="text/javascript">
55170  */
55171
55172
55173 /**
55174  * @class Roo.XTemplate
55175  * @extends Roo.Template
55176  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
55177 <pre><code>
55178 var t = new Roo.XTemplate(
55179         '&lt;select name="{name}"&gt;',
55180                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
55181         '&lt;/select&gt;'
55182 );
55183  
55184 // then append, applying the master template values
55185  </code></pre>
55186  *
55187  * Supported features:
55188  *
55189  *  Tags:
55190
55191 <pre><code>
55192       {a_variable} - output encoded.
55193       {a_variable.format:("Y-m-d")} - call a method on the variable
55194       {a_variable:raw} - unencoded output
55195       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
55196       {a_variable:this.method_on_template(...)} - call a method on the template object.
55197  
55198 </code></pre>
55199  *  The tpl tag:
55200 <pre><code>
55201         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
55202         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
55203         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
55204         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
55205   
55206         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
55207         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
55208 </code></pre>
55209  *      
55210  */
55211 Roo.XTemplate = function()
55212 {
55213     Roo.XTemplate.superclass.constructor.apply(this, arguments);
55214     if (this.html) {
55215         this.compile();
55216     }
55217 };
55218
55219
55220 Roo.extend(Roo.XTemplate, Roo.Template, {
55221
55222     /**
55223      * The various sub templates
55224      */
55225     tpls : false,
55226     /**
55227      *
55228      * basic tag replacing syntax
55229      * WORD:WORD()
55230      *
55231      * // you can fake an object call by doing this
55232      *  x.t:(test,tesT) 
55233      * 
55234      */
55235     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
55236
55237     /**
55238      * compile the template
55239      *
55240      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
55241      *
55242      */
55243     compile: function()
55244     {
55245         var s = this.html;
55246      
55247         s = ['<tpl>', s, '</tpl>'].join('');
55248     
55249         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
55250             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
55251             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
55252             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
55253             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
55254             m,
55255             id     = 0,
55256             tpls   = [];
55257     
55258         while(true == !!(m = s.match(re))){
55259             var forMatch   = m[0].match(nameRe),
55260                 ifMatch   = m[0].match(ifRe),
55261                 execMatch   = m[0].match(execRe),
55262                 namedMatch   = m[0].match(namedRe),
55263                 
55264                 exp  = null, 
55265                 fn   = null,
55266                 exec = null,
55267                 name = forMatch && forMatch[1] ? forMatch[1] : '';
55268                 
55269             if (ifMatch) {
55270                 // if - puts fn into test..
55271                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
55272                 if(exp){
55273                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
55274                 }
55275             }
55276             
55277             if (execMatch) {
55278                 // exec - calls a function... returns empty if true is  returned.
55279                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
55280                 if(exp){
55281                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
55282                 }
55283             }
55284             
55285             
55286             if (name) {
55287                 // for = 
55288                 switch(name){
55289                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
55290                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
55291                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
55292                 }
55293             }
55294             var uid = namedMatch ? namedMatch[1] : id;
55295             
55296             
55297             tpls.push({
55298                 id:     namedMatch ? namedMatch[1] : id,
55299                 target: name,
55300                 exec:   exec,
55301                 test:   fn,
55302                 body:   m[1] || ''
55303             });
55304             if (namedMatch) {
55305                 s = s.replace(m[0], '');
55306             } else { 
55307                 s = s.replace(m[0], '{xtpl'+ id + '}');
55308             }
55309             ++id;
55310         }
55311         this.tpls = [];
55312         for(var i = tpls.length-1; i >= 0; --i){
55313             this.compileTpl(tpls[i]);
55314             this.tpls[tpls[i].id] = tpls[i];
55315         }
55316         this.master = tpls[tpls.length-1];
55317         return this;
55318     },
55319     /**
55320      * same as applyTemplate, except it's done to one of the subTemplates
55321      * when using named templates, you can do:
55322      *
55323      * var str = pl.applySubTemplate('your-name', values);
55324      *
55325      * 
55326      * @param {Number} id of the template
55327      * @param {Object} values to apply to template
55328      * @param {Object} parent (normaly the instance of this object)
55329      */
55330     applySubTemplate : function(id, values, parent)
55331     {
55332         
55333         
55334         var t = this.tpls[id];
55335         
55336         
55337         try { 
55338             if(t.test && !t.test.call(this, values, parent)){
55339                 return '';
55340             }
55341         } catch(e) {
55342             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
55343             Roo.log(e.toString());
55344             Roo.log(t.test);
55345             return ''
55346         }
55347         try { 
55348             
55349             if(t.exec && t.exec.call(this, values, parent)){
55350                 return '';
55351             }
55352         } catch(e) {
55353             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
55354             Roo.log(e.toString());
55355             Roo.log(t.exec);
55356             return ''
55357         }
55358         try {
55359             var vs = t.target ? t.target.call(this, values, parent) : values;
55360             parent = t.target ? values : parent;
55361             if(t.target && vs instanceof Array){
55362                 var buf = [];
55363                 for(var i = 0, len = vs.length; i < len; i++){
55364                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
55365                 }
55366                 return buf.join('');
55367             }
55368             return t.compiled.call(this, vs, parent);
55369         } catch (e) {
55370             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
55371             Roo.log(e.toString());
55372             Roo.log(t.compiled);
55373             return '';
55374         }
55375     },
55376
55377     compileTpl : function(tpl)
55378     {
55379         var fm = Roo.util.Format;
55380         var useF = this.disableFormats !== true;
55381         var sep = Roo.isGecko ? "+" : ",";
55382         var undef = function(str) {
55383             Roo.log("Property not found :"  + str);
55384             return '';
55385         };
55386         
55387         var fn = function(m, name, format, args)
55388         {
55389             //Roo.log(arguments);
55390             args = args ? args.replace(/\\'/g,"'") : args;
55391             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
55392             if (typeof(format) == 'undefined') {
55393                 format= 'htmlEncode';
55394             }
55395             if (format == 'raw' ) {
55396                 format = false;
55397             }
55398             
55399             if(name.substr(0, 4) == 'xtpl'){
55400                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
55401             }
55402             
55403             // build an array of options to determine if value is undefined..
55404             
55405             // basically get 'xxxx.yyyy' then do
55406             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
55407             //    (function () { Roo.log("Property not found"); return ''; })() :
55408             //    ......
55409             
55410             var udef_ar = [];
55411             var lookfor = '';
55412             Roo.each(name.split('.'), function(st) {
55413                 lookfor += (lookfor.length ? '.': '') + st;
55414                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
55415             });
55416             
55417             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
55418             
55419             
55420             if(format && useF){
55421                 
55422                 args = args ? ',' + args : "";
55423                  
55424                 if(format.substr(0, 5) != "this."){
55425                     format = "fm." + format + '(';
55426                 }else{
55427                     format = 'this.call("'+ format.substr(5) + '", ';
55428                     args = ", values";
55429                 }
55430                 
55431                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
55432             }
55433              
55434             if (args.length) {
55435                 // called with xxyx.yuu:(test,test)
55436                 // change to ()
55437                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
55438             }
55439             // raw.. - :raw modifier..
55440             return "'"+ sep + udef_st  + name + ")"+sep+"'";
55441             
55442         };
55443         var body;
55444         // branched to use + in gecko and [].join() in others
55445         if(Roo.isGecko){
55446             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
55447                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
55448                     "';};};";
55449         }else{
55450             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
55451             body.push(tpl.body.replace(/(\r\n|\n)/g,
55452                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
55453             body.push("'].join('');};};");
55454             body = body.join('');
55455         }
55456         
55457         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
55458        
55459         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
55460         eval(body);
55461         
55462         return this;
55463     },
55464
55465     applyTemplate : function(values){
55466         return this.master.compiled.call(this, values, {});
55467         //var s = this.subs;
55468     },
55469
55470     apply : function(){
55471         return this.applyTemplate.apply(this, arguments);
55472     }
55473
55474  });
55475
55476 Roo.XTemplate.from = function(el){
55477     el = Roo.getDom(el);
55478     return new Roo.XTemplate(el.value || el.innerHTML);
55479 };/*
55480  * Original code for Roojs - LGPL
55481  * <script type="text/javascript">
55482  */
55483  
55484 /**
55485  * @class Roo.XComponent
55486  * A delayed Element creator...
55487  * Or a way to group chunks of interface together.
55488  * 
55489  * Mypart.xyx = new Roo.XComponent({
55490
55491     parent : 'Mypart.xyz', // empty == document.element.!!
55492     order : '001',
55493     name : 'xxxx'
55494     region : 'xxxx'
55495     disabled : function() {} 
55496      
55497     tree : function() { // return an tree of xtype declared components
55498         var MODULE = this;
55499         return 
55500         {
55501             xtype : 'NestedLayoutPanel',
55502             // technicall
55503         }
55504      ]
55505  *})
55506  *
55507  *
55508  * It can be used to build a big heiracy, with parent etc.
55509  * or you can just use this to render a single compoent to a dom element
55510  * MYPART.render(Roo.Element | String(id) | dom_element )
55511  * 
55512  * @extends Roo.util.Observable
55513  * @constructor
55514  * @param cfg {Object} configuration of component
55515  * 
55516  */
55517 Roo.XComponent = function(cfg) {
55518     Roo.apply(this, cfg);
55519     this.addEvents({ 
55520         /**
55521              * @event built
55522              * Fires when this the componnt is built
55523              * @param {Roo.XComponent} c the component
55524              */
55525         'built' : true
55526         
55527     });
55528     this.region = this.region || 'center'; // default..
55529     Roo.XComponent.register(this);
55530     this.modules = false;
55531     this.el = false; // where the layout goes..
55532     
55533     
55534 }
55535 Roo.extend(Roo.XComponent, Roo.util.Observable, {
55536     /**
55537      * @property el
55538      * The created element (with Roo.factory())
55539      * @type {Roo.Layout}
55540      */
55541     el  : false,
55542     
55543     /**
55544      * @property el
55545      * for BC  - use el in new code
55546      * @type {Roo.Layout}
55547      */
55548     panel : false,
55549     
55550     /**
55551      * @property layout
55552      * for BC  - use el in new code
55553      * @type {Roo.Layout}
55554      */
55555     layout : false,
55556     
55557      /**
55558      * @cfg {Function|boolean} disabled
55559      * If this module is disabled by some rule, return true from the funtion
55560      */
55561     disabled : false,
55562     
55563     /**
55564      * @cfg {String} parent 
55565      * Name of parent element which it get xtype added to..
55566      */
55567     parent: false,
55568     
55569     /**
55570      * @cfg {String} order
55571      * Used to set the order in which elements are created (usefull for multiple tabs)
55572      */
55573     
55574     order : false,
55575     /**
55576      * @cfg {String} name
55577      * String to display while loading.
55578      */
55579     name : false,
55580     /**
55581      * @cfg {String} region
55582      * Region to render component to (defaults to center)
55583      */
55584     region : 'center',
55585     
55586     /**
55587      * @cfg {Array} items
55588      * A single item array - the first element is the root of the tree..
55589      * It's done this way to stay compatible with the Xtype system...
55590      */
55591     items : false,
55592     
55593     /**
55594      * @property _tree
55595      * The method that retuns the tree of parts that make up this compoennt 
55596      * @type {function}
55597      */
55598     _tree  : false,
55599     
55600      /**
55601      * render
55602      * render element to dom or tree
55603      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
55604      */
55605     
55606     render : function(el)
55607     {
55608         
55609         el = el || false;
55610         var hp = this.parent ? 1 : 0;
55611         
55612         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
55613             // if parent is a '#.....' string, then let's use that..
55614             var ename = this.parent.substr(1)
55615             this.parent = false;
55616             el = Roo.get(ename);
55617             if (!el) {
55618                 Roo.log("Warning - element can not be found :#" + ename );
55619                 return;
55620             }
55621         }
55622         
55623         
55624         if (!this.parent) {
55625             
55626             el = el ? Roo.get(el) : false;      
55627             
55628             // it's a top level one..
55629             this.parent =  {
55630                 el : new Roo.BorderLayout(el || document.body, {
55631                 
55632                      center: {
55633                          titlebar: false,
55634                          autoScroll:false,
55635                          closeOnTab: true,
55636                          tabPosition: 'top',
55637                           //resizeTabs: true,
55638                          alwaysShowTabs: el && hp? false :  true,
55639                          hideTabs: el || !hp ? true :  false,
55640                          minTabWidth: 140
55641                      }
55642                  })
55643             }
55644         }
55645         
55646                 if (!this.parent.el) {
55647                         // probably an old style ctor, which has been disabled.
55648                         return;
55649                         
55650                 }
55651                 // The 'tree' method is  '_tree now' 
55652             
55653         var tree = this._tree ? this._tree() : this.tree();
55654         tree.region = tree.region || this.region;
55655         this.el = this.parent.el.addxtype(tree);
55656         this.fireEvent('built', this);
55657         
55658         this.panel = this.el;
55659         this.layout = this.panel.layout;
55660                 this.parentLayout = this.parent.layout  || false;  
55661          
55662     }
55663     
55664 });
55665
55666 Roo.apply(Roo.XComponent, {
55667     /**
55668      * @property  hideProgress
55669      * true to disable the building progress bar.. usefull on single page renders.
55670      * @type Boolean
55671      */
55672     hideProgress : false,
55673     /**
55674      * @property  buildCompleted
55675      * True when the builder has completed building the interface.
55676      * @type Boolean
55677      */
55678     buildCompleted : false,
55679      
55680     /**
55681      * @property  topModule
55682      * the upper most module - uses document.element as it's constructor.
55683      * @type Object
55684      */
55685      
55686     topModule  : false,
55687       
55688     /**
55689      * @property  modules
55690      * array of modules to be created by registration system.
55691      * @type {Array} of Roo.XComponent
55692      */
55693     
55694     modules : [],
55695     /**
55696      * @property  elmodules
55697      * array of modules to be created by which use #ID 
55698      * @type {Array} of Roo.XComponent
55699      */
55700      
55701     elmodules : [],
55702
55703     
55704     /**
55705      * Register components to be built later.
55706      *
55707      * This solves the following issues
55708      * - Building is not done on page load, but after an authentication process has occured.
55709      * - Interface elements are registered on page load
55710      * - Parent Interface elements may not be loaded before child, so this handles that..
55711      * 
55712      *
55713      * example:
55714      * 
55715      * MyApp.register({
55716           order : '000001',
55717           module : 'Pman.Tab.projectMgr',
55718           region : 'center',
55719           parent : 'Pman.layout',
55720           disabled : false,  // or use a function..
55721         })
55722      
55723      * * @param {Object} details about module
55724      */
55725     register : function(obj) {
55726                 
55727         Roo.XComponent.event.fireEvent('register', obj);
55728         switch(typeof(obj.disabled) ) {
55729                 
55730             case 'undefined':
55731                 break;
55732             
55733             case 'function':
55734                 if ( obj.disabled() ) {
55735                         return;
55736                 }
55737                 break;
55738             
55739             default:
55740                 if (obj.disabled) {
55741                         return;
55742                 }
55743                 break;
55744         }
55745                 
55746         this.modules.push(obj);
55747          
55748     },
55749     /**
55750      * convert a string to an object..
55751      * eg. 'AAA.BBB' -> finds AAA.BBB
55752
55753      */
55754     
55755     toObject : function(str)
55756     {
55757         if (!str || typeof(str) == 'object') {
55758             return str;
55759         }
55760         if (str.substring(0,1) == '#') {
55761             return str;
55762         }
55763
55764         var ar = str.split('.');
55765         var rt, o;
55766         rt = ar.shift();
55767             /** eval:var:o */
55768         try {
55769             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
55770         } catch (e) {
55771             throw "Module not found : " + str;
55772         }
55773         
55774         if (o === false) {
55775             throw "Module not found : " + str;
55776         }
55777         Roo.each(ar, function(e) {
55778             if (typeof(o[e]) == 'undefined') {
55779                 throw "Module not found : " + str;
55780             }
55781             o = o[e];
55782         });
55783         
55784         return o;
55785         
55786     },
55787     
55788     
55789     /**
55790      * move modules into their correct place in the tree..
55791      * 
55792      */
55793     preBuild : function ()
55794     {
55795         var _t = this;
55796         Roo.each(this.modules , function (obj)
55797         {
55798             Roo.XComponent.event.fireEvent('beforebuild', obj);
55799             
55800             var opar = obj.parent;
55801             try { 
55802                 obj.parent = this.toObject(opar);
55803             } catch(e) {
55804                 Roo.log("parent:toObject failed: " + e.toString());
55805                 return;
55806             }
55807             
55808             if (!obj.parent) {
55809                 Roo.debug && Roo.log("GOT top level module");
55810                 Roo.debug && Roo.log(obj);
55811                 obj.modules = new Roo.util.MixedCollection(false, 
55812                     function(o) { return o.order + '' }
55813                 );
55814                 this.topModule = obj;
55815                 return;
55816             }
55817                         // parent is a string (usually a dom element name..)
55818             if (typeof(obj.parent) == 'string') {
55819                 this.elmodules.push(obj);
55820                 return;
55821             }
55822             if (obj.parent.constructor != Roo.XComponent) {
55823                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
55824             }
55825             if (!obj.parent.modules) {
55826                 obj.parent.modules = new Roo.util.MixedCollection(false, 
55827                     function(o) { return o.order + '' }
55828                 );
55829             }
55830             if (obj.parent.disabled) {
55831                 obj.disabled = true;
55832             }
55833             obj.parent.modules.add(obj);
55834         }, this);
55835     },
55836     
55837      /**
55838      * make a list of modules to build.
55839      * @return {Array} list of modules. 
55840      */ 
55841     
55842     buildOrder : function()
55843     {
55844         var _this = this;
55845         var cmp = function(a,b) {   
55846             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
55847         };
55848         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
55849             throw "No top level modules to build";
55850         }
55851         
55852         // make a flat list in order of modules to build.
55853         var mods = this.topModule ? [ this.topModule ] : [];
55854                 
55855         
55856         // elmodules (is a list of DOM based modules )
55857         Roo.each(this.elmodules, function(e) {
55858             mods.push(e);
55859             if (!this.topModule &&
55860                 typeof(e.parent) == 'string' &&
55861                 e.parent.substring(0,1) == '#' &&
55862                 Roo.get(e.parent.substr(1))
55863                ) {
55864                 
55865                 _this.topModule = e;
55866             }
55867             
55868         });
55869
55870         
55871         // add modules to their parents..
55872         var addMod = function(m) {
55873             Roo.debug && Roo.log("build Order: add: " + m.name);
55874                 
55875             mods.push(m);
55876             if (m.modules && !m.disabled) {
55877                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
55878                 m.modules.keySort('ASC',  cmp );
55879                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
55880     
55881                 m.modules.each(addMod);
55882             } else {
55883                 Roo.debug && Roo.log("build Order: no child modules");
55884             }
55885             // not sure if this is used any more..
55886             if (m.finalize) {
55887                 m.finalize.name = m.name + " (clean up) ";
55888                 mods.push(m.finalize);
55889             }
55890             
55891         }
55892         if (this.topModule && this.topModule.modules) { 
55893             this.topModule.modules.keySort('ASC',  cmp );
55894             this.topModule.modules.each(addMod);
55895         } 
55896         return mods;
55897     },
55898     
55899      /**
55900      * Build the registered modules.
55901      * @param {Object} parent element.
55902      * @param {Function} optional method to call after module has been added.
55903      * 
55904      */ 
55905    
55906     build : function() 
55907     {
55908         
55909         this.preBuild();
55910         var mods = this.buildOrder();
55911       
55912         //this.allmods = mods;
55913         //Roo.debug && Roo.log(mods);
55914         //return;
55915         if (!mods.length) { // should not happen
55916             throw "NO modules!!!";
55917         }
55918         
55919         
55920         var msg = "Building Interface...";
55921         // flash it up as modal - so we store the mask!?
55922         if (!this.hideProgress) {
55923             Roo.MessageBox.show({ title: 'loading' });
55924             Roo.MessageBox.show({
55925                title: "Please wait...",
55926                msg: msg,
55927                width:450,
55928                progress:true,
55929                closable:false,
55930                modal: false
55931               
55932             });
55933         }
55934         var total = mods.length;
55935         
55936         var _this = this;
55937         var progressRun = function() {
55938             if (!mods.length) {
55939                 Roo.debug && Roo.log('hide?');
55940                 if (!this.hideProgress) {
55941                     Roo.MessageBox.hide();
55942                 }
55943                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
55944                 
55945                 // THE END...
55946                 return false;   
55947             }
55948             
55949             var m = mods.shift();
55950             
55951             
55952             Roo.debug && Roo.log(m);
55953             // not sure if this is supported any more.. - modules that are are just function
55954             if (typeof(m) == 'function') { 
55955                 m.call(this);
55956                 return progressRun.defer(10, _this);
55957             } 
55958             
55959             
55960             msg = "Building Interface " + (total  - mods.length) + 
55961                     " of " + total + 
55962                     (m.name ? (' - ' + m.name) : '');
55963                         Roo.debug && Roo.log(msg);
55964             if (!this.hideProgress) { 
55965                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
55966             }
55967             
55968          
55969             // is the module disabled?
55970             var disabled = (typeof(m.disabled) == 'function') ?
55971                 m.disabled.call(m.module.disabled) : m.disabled;    
55972             
55973             
55974             if (disabled) {
55975                 return progressRun(); // we do not update the display!
55976             }
55977             
55978             // now build 
55979             
55980                         
55981                         
55982             m.render();
55983             // it's 10 on top level, and 1 on others??? why...
55984             return progressRun.defer(10, _this);
55985              
55986         }
55987         progressRun.defer(1, _this);
55988      
55989         
55990         
55991     },
55992         
55993         
55994         /**
55995          * Event Object.
55996          *
55997          *
55998          */
55999         event: false, 
56000     /**
56001          * wrapper for event.on - aliased later..  
56002          * Typically use to register a event handler for register:
56003          *
56004          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
56005          *
56006          */
56007     on : false
56008    
56009     
56010     
56011 });
56012
56013 Roo.XComponent.event = new Roo.util.Observable({
56014                 events : { 
56015                         /**
56016                          * @event register
56017                          * Fires when an Component is registered,
56018                          * set the disable property on the Component to stop registration.
56019                          * @param {Roo.XComponent} c the component being registerd.
56020                          * 
56021                          */
56022                         'register' : true,
56023             /**
56024                          * @event beforebuild
56025                          * Fires before each Component is built
56026                          * can be used to apply permissions.
56027                          * @param {Roo.XComponent} c the component being registerd.
56028                          * 
56029                          */
56030                         'beforebuild' : true,
56031                         /**
56032                          * @event buildcomplete
56033                          * Fires on the top level element when all elements have been built
56034                          * @param {Roo.XComponent} the top level component.
56035                          */
56036                         'buildcomplete' : true
56037                         
56038                 }
56039 });
56040
56041 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
56042