roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isIOS = /iphone|ipad/.test(ua),
67         isTouch =  (function() {
68             try {  
69                 document.createEvent("TouchEvent");  
70                 return true;  
71             } catch (e) {  
72                 return false;  
73             } 
74             
75         })();
76     // remove css image flicker
77         if(isIE && !isIE7){
78         try{
79             document.execCommand("BackgroundImageCache", false, true);
80         }catch(e){}
81     }
82     
83     Roo.apply(Roo, {
84         /**
85          * True if the browser is in strict mode
86          * @type Boolean
87          */
88         isStrict : isStrict,
89         /**
90          * True if the page is running over SSL
91          * @type Boolean
92          */
93         isSecure : isSecure,
94         /**
95          * True when the document is fully initialized and ready for action
96          * @type Boolean
97          */
98         isReady : false,
99         /**
100          * Turn on debugging output (currently only the factory uses this)
101          * @type Boolean
102          */
103         
104         debug: false,
105
106         /**
107          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
108          * @type Boolean
109          */
110         enableGarbageCollector : true,
111
112         /**
113          * True to automatically purge event listeners after uncaching an element (defaults to false).
114          * Note: this only happens if enableGarbageCollector is true.
115          * @type Boolean
116          */
117         enableListenerCollection:false,
118
119         /**
120          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
121          * the IE insecure content warning (defaults to javascript:false).
122          * @type String
123          */
124         SSL_SECURE_URL : "javascript:false",
125
126         /**
127          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
128          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
129          * @type String
130          */
131         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
132
133         emptyFn : function(){},
134         
135         /**
136          * Copies all the properties of config to obj if they don't already exist.
137          * @param {Object} obj The receiver of the properties
138          * @param {Object} config The source of the properties
139          * @return {Object} returns obj
140          */
141         applyIf : function(o, c){
142             if(o && c){
143                 for(var p in c){
144                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
145                 }
146             }
147             return o;
148         },
149
150         /**
151          * Applies event listeners to elements by selectors when the document is ready.
152          * The event name is specified with an @ suffix.
153 <pre><code>
154 Roo.addBehaviors({
155    // add a listener for click on all anchors in element with id foo
156    '#foo a@click' : function(e, t){
157        // do something
158    },
159
160    // add the same listener to multiple selectors (separated by comma BEFORE the @)
161    '#foo a, #bar span.some-class@mouseover' : function(){
162        // do something
163    }
164 });
165 </code></pre>
166          * @param {Object} obj The list of behaviors to apply
167          */
168         addBehaviors : function(o){
169             if(!Roo.isReady){
170                 Roo.onReady(function(){
171                     Roo.addBehaviors(o);
172                 });
173                 return;
174             }
175             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
176             for(var b in o){
177                 var parts = b.split('@');
178                 if(parts[1]){ // for Object prototype breakers
179                     var s = parts[0];
180                     if(!cache[s]){
181                         cache[s] = Roo.select(s);
182                     }
183                     cache[s].on(parts[1], o[b]);
184                 }
185             }
186             cache = null;
187         },
188
189         /**
190          * Generates unique ids. If the element already has an id, it is unchanged
191          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
192          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
193          * @return {String} The generated Id.
194          */
195         id : function(el, prefix){
196             prefix = prefix || "roo-gen";
197             el = Roo.getDom(el);
198             var id = prefix + (++idSeed);
199             return el ? (el.id ? el.id : (el.id = id)) : id;
200         },
201          
202        
203         /**
204          * Extends one class with another class and optionally overrides members with the passed literal. This class
205          * also adds the function "override()" to the class that can be used to override
206          * members on an instance.
207          * @param {Object} subclass The class inheriting the functionality
208          * @param {Object} superclass The class being extended
209          * @param {Object} overrides (optional) A literal with members
210          * @method extend
211          */
212         extend : function(){
213             // inline overrides
214             var io = function(o){
215                 for(var m in o){
216                     this[m] = o[m];
217                 }
218             };
219             return function(sb, sp, overrides){
220                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
221                     overrides = sp;
222                     sp = sb;
223                     sb = function(){sp.apply(this, arguments);};
224                 }
225                 var F = function(){}, sbp, spp = sp.prototype;
226                 F.prototype = spp;
227                 sbp = sb.prototype = new F();
228                 sbp.constructor=sb;
229                 sb.superclass=spp;
230                 
231                 if(spp.constructor == Object.prototype.constructor){
232                     spp.constructor=sp;
233                    
234                 }
235                 
236                 sb.override = function(o){
237                     Roo.override(sb, o);
238                 };
239                 sbp.override = io;
240                 Roo.override(sb, overrides);
241                 return sb;
242             };
243         }(),
244
245         /**
246          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
247          * Usage:<pre><code>
248 Roo.override(MyClass, {
249     newMethod1: function(){
250         // etc.
251     },
252     newMethod2: function(foo){
253         // etc.
254     }
255 });
256  </code></pre>
257          * @param {Object} origclass The class to override
258          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
259          * containing one or more methods.
260          * @method override
261          */
262         override : function(origclass, overrides){
263             if(overrides){
264                 var p = origclass.prototype;
265                 for(var method in overrides){
266                     p[method] = overrides[method];
267                 }
268             }
269         },
270         /**
271          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
272          * <pre><code>
273 Roo.namespace('Company', 'Company.data');
274 Company.Widget = function() { ... }
275 Company.data.CustomStore = function(config) { ... }
276 </code></pre>
277          * @param {String} namespace1
278          * @param {String} namespace2
279          * @param {String} etc
280          * @method namespace
281          */
282         namespace : function(){
283             var a=arguments, o=null, i, j, d, rt;
284             for (i=0; i<a.length; ++i) {
285                 d=a[i].split(".");
286                 rt = d[0];
287                 /** eval:var:o */
288                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
289                 for (j=1; j<d.length; ++j) {
290                     o[d[j]]=o[d[j]] || {};
291                     o=o[d[j]];
292                 }
293             }
294         },
295         /**
296          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
297          * <pre><code>
298 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
299 Roo.factory(conf, Roo.data);
300 </code></pre>
301          * @param {String} classname
302          * @param {String} namespace (optional)
303          * @method factory
304          */
305          
306         factory : function(c, ns)
307         {
308             // no xtype, no ns or c.xns - or forced off by c.xns
309             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
310                 return c;
311             }
312             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
313             if (c.constructor == ns[c.xtype]) {// already created...
314                 return c;
315             }
316             if (ns[c.xtype]) {
317                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
318                 var ret = new ns[c.xtype](c);
319                 ret.xns = false;
320                 return ret;
321             }
322             c.xns = false; // prevent recursion..
323             return c;
324         },
325          /**
326          * Logs to console if it can.
327          *
328          * @param {String|Object} string
329          * @method log
330          */
331         log : function(s)
332         {
333             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
334                 return; // alerT?
335             }
336             console.log(s);
337             
338         },
339         /**
340          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
341          * @param {Object} o
342          * @return {String}
343          */
344         urlEncode : function(o){
345             if(!o){
346                 return "";
347             }
348             var buf = [];
349             for(var key in o){
350                 var ov = o[key], k = Roo.encodeURIComponent(key);
351                 var type = typeof ov;
352                 if(type == 'undefined'){
353                     buf.push(k, "=&");
354                 }else if(type != "function" && type != "object"){
355                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
356                 }else if(ov instanceof Array){
357                     if (ov.length) {
358                             for(var i = 0, len = ov.length; i < len; i++) {
359                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
360                             }
361                         } else {
362                             buf.push(k, "=&");
363                         }
364                 }
365             }
366             buf.pop();
367             return buf.join("");
368         },
369          /**
370          * Safe version of encodeURIComponent
371          * @param {String} data 
372          * @return {String} 
373          */
374         
375         encodeURIComponent : function (data)
376         {
377             try {
378                 return encodeURIComponent(data);
379             } catch(e) {} // should be an uri encode error.
380             
381             if (data == '' || data == null){
382                return '';
383             }
384             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
385             function nibble_to_hex(nibble){
386                 var chars = '0123456789ABCDEF';
387                 return chars.charAt(nibble);
388             }
389             data = data.toString();
390             var buffer = '';
391             for(var i=0; i<data.length; i++){
392                 var c = data.charCodeAt(i);
393                 var bs = new Array();
394                 if (c > 0x10000){
395                         // 4 bytes
396                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
397                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
398                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
399                     bs[3] = 0x80 | (c & 0x3F);
400                 }else if (c > 0x800){
401                          // 3 bytes
402                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
403                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
404                     bs[2] = 0x80 | (c & 0x3F);
405                 }else if (c > 0x80){
406                        // 2 bytes
407                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
408                     bs[1] = 0x80 | (c & 0x3F);
409                 }else{
410                         // 1 byte
411                     bs[0] = c;
412                 }
413                 for(var j=0; j<bs.length; j++){
414                     var b = bs[j];
415                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
416                             + nibble_to_hex(b &0x0F);
417                     buffer += '%'+hex;
418                }
419             }
420             return buffer;    
421              
422         },
423
424         /**
425          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
426          * @param {String} string
427          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
428          * @return {Object} A literal with members
429          */
430         urlDecode : function(string, overwrite){
431             if(!string || !string.length){
432                 return {};
433             }
434             var obj = {};
435             var pairs = string.split('&');
436             var pair, name, value;
437             for(var i = 0, len = pairs.length; i < len; i++){
438                 pair = pairs[i].split('=');
439                 name = decodeURIComponent(pair[0]);
440                 value = decodeURIComponent(pair[1]);
441                 if(overwrite !== true){
442                     if(typeof obj[name] == "undefined"){
443                         obj[name] = value;
444                     }else if(typeof obj[name] == "string"){
445                         obj[name] = [obj[name]];
446                         obj[name].push(value);
447                     }else{
448                         obj[name].push(value);
449                     }
450                 }else{
451                     obj[name] = value;
452                 }
453             }
454             return obj;
455         },
456
457         /**
458          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
459          * passed array is not really an array, your function is called once with it.
460          * The supplied function is called with (Object item, Number index, Array allItems).
461          * @param {Array/NodeList/Mixed} array
462          * @param {Function} fn
463          * @param {Object} scope
464          */
465         each : function(array, fn, scope){
466             if(typeof array.length == "undefined" || typeof array == "string"){
467                 array = [array];
468             }
469             for(var i = 0, len = array.length; i < len; i++){
470                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
471             }
472         },
473
474         // deprecated
475         combine : function(){
476             var as = arguments, l = as.length, r = [];
477             for(var i = 0; i < l; i++){
478                 var a = as[i];
479                 if(a instanceof Array){
480                     r = r.concat(a);
481                 }else if(a.length !== undefined && !a.substr){
482                     r = r.concat(Array.prototype.slice.call(a, 0));
483                 }else{
484                     r.push(a);
485                 }
486             }
487             return r;
488         },
489
490         /**
491          * Escapes the passed string for use in a regular expression
492          * @param {String} str
493          * @return {String}
494          */
495         escapeRe : function(s) {
496             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
497         },
498
499         // internal
500         callback : function(cb, scope, args, delay){
501             if(typeof cb == "function"){
502                 if(delay){
503                     cb.defer(delay, scope, args || []);
504                 }else{
505                     cb.apply(scope, args || []);
506                 }
507             }
508         },
509
510         /**
511          * Return the dom node for the passed string (id), dom node, or Roo.Element
512          * @param {String/HTMLElement/Roo.Element} el
513          * @return HTMLElement
514          */
515         getDom : function(el){
516             if(!el){
517                 return null;
518             }
519             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
520         },
521
522         /**
523         * Shorthand for {@link Roo.ComponentMgr#get}
524         * @param {String} id
525         * @return Roo.Component
526         */
527         getCmp : function(id){
528             return Roo.ComponentMgr.get(id);
529         },
530          
531         num : function(v, defaultValue){
532             if(typeof v != 'number'){
533                 return defaultValue;
534             }
535             return v;
536         },
537
538         destroy : function(){
539             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
540                 var as = a[i];
541                 if(as){
542                     if(as.dom){
543                         as.removeAllListeners();
544                         as.remove();
545                         continue;
546                     }
547                     if(typeof as.purgeListeners == 'function'){
548                         as.purgeListeners();
549                     }
550                     if(typeof as.destroy == 'function'){
551                         as.destroy();
552                     }
553                 }
554             }
555         },
556
557         // inpired by a similar function in mootools library
558         /**
559          * Returns the type of object that is passed in. If the object passed in is null or undefined it
560          * return false otherwise it returns one of the following values:<ul>
561          * <li><b>string</b>: If the object passed is a string</li>
562          * <li><b>number</b>: If the object passed is a number</li>
563          * <li><b>boolean</b>: If the object passed is a boolean value</li>
564          * <li><b>function</b>: If the object passed is a function reference</li>
565          * <li><b>object</b>: If the object passed is an object</li>
566          * <li><b>array</b>: If the object passed is an array</li>
567          * <li><b>regexp</b>: If the object passed is a regular expression</li>
568          * <li><b>element</b>: If the object passed is a DOM Element</li>
569          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
570          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
571          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
572          * @param {Mixed} object
573          * @return {String}
574          */
575         type : function(o){
576             if(o === undefined || o === null){
577                 return false;
578             }
579             if(o.htmlElement){
580                 return 'element';
581             }
582             var t = typeof o;
583             if(t == 'object' && o.nodeName) {
584                 switch(o.nodeType) {
585                     case 1: return 'element';
586                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
587                 }
588             }
589             if(t == 'object' || t == 'function') {
590                 switch(o.constructor) {
591                     case Array: return 'array';
592                     case RegExp: return 'regexp';
593                 }
594                 if(typeof o.length == 'number' && typeof o.item == 'function') {
595                     return 'nodelist';
596                 }
597             }
598             return t;
599         },
600
601         /**
602          * Returns true if the passed value is null, undefined or an empty string (optional).
603          * @param {Mixed} value The value to test
604          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
605          * @return {Boolean}
606          */
607         isEmpty : function(v, allowBlank){
608             return v === null || v === undefined || (!allowBlank ? v === '' : false);
609         },
610         
611         /** @type Boolean */
612         isOpera : isOpera,
613         /** @type Boolean */
614         isSafari : isSafari,
615         /** @type Boolean */
616         isFirefox : isFirefox,
617         /** @type Boolean */
618         isIE : isIE,
619         /** @type Boolean */
620         isIE7 : isIE7,
621         /** @type Boolean */
622         isIE11 : isIE11,
623         /** @type Boolean */
624         isGecko : isGecko,
625         /** @type Boolean */
626         isBorderBox : isBorderBox,
627         /** @type Boolean */
628         isWindows : isWindows,
629         /** @type Boolean */
630         isLinux : isLinux,
631         /** @type Boolean */
632         isMac : isMac,
633         /** @type Boolean */
634         isIOS : isIOS,
635         /** @type Boolean */
636         isTouch : isTouch,
637
638         /**
639          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
640          * you may want to set this to true.
641          * @type Boolean
642          */
643         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
644         
645         
646                 
647         /**
648          * Selects a single element as a Roo Element
649          * This is about as close as you can get to jQuery's $('do crazy stuff')
650          * @param {String} selector The selector/xpath query
651          * @param {Node} root (optional) The start of the query (defaults to document).
652          * @return {Roo.Element}
653          */
654         selectNode : function(selector, root) 
655         {
656             var node = Roo.DomQuery.selectNode(selector,root);
657             return node ? Roo.get(node) : new Roo.Element(false);
658         }
659         
660     });
661
662
663 })();
664
665 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
666                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
667                 "Roo.app", "Roo.ux",
668                 "Roo.bootstrap",
669                 "Roo.bootstrap.dash");
670 /*
671  * Based on:
672  * Ext JS Library 1.1.1
673  * Copyright(c) 2006-2007, Ext JS, LLC.
674  *
675  * Originally Released Under LGPL - original licence link has changed is not relivant.
676  *
677  * Fork - LGPL
678  * <script type="text/javascript">
679  */
680
681 (function() {    
682     // wrappedn so fnCleanup is not in global scope...
683     if(Roo.isIE) {
684         function fnCleanUp() {
685             var p = Function.prototype;
686             delete p.createSequence;
687             delete p.defer;
688             delete p.createDelegate;
689             delete p.createCallback;
690             delete p.createInterceptor;
691
692             window.detachEvent("onunload", fnCleanUp);
693         }
694         window.attachEvent("onunload", fnCleanUp);
695     }
696 })();
697
698
699 /**
700  * @class Function
701  * These functions are available on every Function object (any JavaScript function).
702  */
703 Roo.apply(Function.prototype, {
704      /**
705      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
706      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
707      * Will create a function that is bound to those 2 args.
708      * @return {Function} The new function
709     */
710     createCallback : function(/*args...*/){
711         // make args available, in function below
712         var args = arguments;
713         var method = this;
714         return function() {
715             return method.apply(window, args);
716         };
717     },
718
719     /**
720      * Creates a delegate (callback) that sets the scope to obj.
721      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
722      * Will create a function that is automatically scoped to this.
723      * @param {Object} obj (optional) The object for which the scope is set
724      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
725      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
726      *                                             if a number the args are inserted at the specified position
727      * @return {Function} The new function
728      */
729     createDelegate : function(obj, args, appendArgs){
730         var method = this;
731         return function() {
732             var callArgs = args || arguments;
733             if(appendArgs === true){
734                 callArgs = Array.prototype.slice.call(arguments, 0);
735                 callArgs = callArgs.concat(args);
736             }else if(typeof appendArgs == "number"){
737                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
738                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
739                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
740             }
741             return method.apply(obj || window, callArgs);
742         };
743     },
744
745     /**
746      * Calls this function after the number of millseconds specified.
747      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
748      * @param {Object} obj (optional) The object for which the scope is set
749      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
750      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
751      *                                             if a number the args are inserted at the specified position
752      * @return {Number} The timeout id that can be used with clearTimeout
753      */
754     defer : function(millis, obj, args, appendArgs){
755         var fn = this.createDelegate(obj, args, appendArgs);
756         if(millis){
757             return setTimeout(fn, millis);
758         }
759         fn();
760         return 0;
761     },
762     /**
763      * Create a combined function call sequence of the original function + the passed function.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function
766      * @param {Function} fcn The function to sequence
767      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
768      * @return {Function} The new function
769      */
770     createSequence : function(fcn, scope){
771         if(typeof fcn != "function"){
772             return this;
773         }
774         var method = this;
775         return function() {
776             var retval = method.apply(this || window, arguments);
777             fcn.apply(scope || this || window, arguments);
778             return retval;
779         };
780     },
781
782     /**
783      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
784      * The resulting function returns the results of the original function.
785      * The passed fcn is called with the parameters of the original function.
786      * @addon
787      * @param {Function} fcn The function to call before the original
788      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
789      * @return {Function} The new function
790      */
791     createInterceptor : function(fcn, scope){
792         if(typeof fcn != "function"){
793             return this;
794         }
795         var method = this;
796         return function() {
797             fcn.target = this;
798             fcn.method = method;
799             if(fcn.apply(scope || this || window, arguments) === false){
800                 return;
801             }
802             return method.apply(this || window, arguments);
803         };
804     }
805 });
806 /*
807  * Based on:
808  * Ext JS Library 1.1.1
809  * Copyright(c) 2006-2007, Ext JS, LLC.
810  *
811  * Originally Released Under LGPL - original licence link has changed is not relivant.
812  *
813  * Fork - LGPL
814  * <script type="text/javascript">
815  */
816
817 Roo.applyIf(String, {
818     
819     /** @scope String */
820     
821     /**
822      * Escapes the passed string for ' and \
823      * @param {String} string The string to escape
824      * @return {String} The escaped string
825      * @static
826      */
827     escape : function(string) {
828         return string.replace(/('|\\)/g, "\\$1");
829     },
830
831     /**
832      * Pads the left side of a string with a specified character.  This is especially useful
833      * for normalizing number and date strings.  Example usage:
834      * <pre><code>
835 var s = String.leftPad('123', 5, '0');
836 // s now contains the string: '00123'
837 </code></pre>
838      * @param {String} string The original string
839      * @param {Number} size The total length of the output string
840      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
841      * @return {String} The padded string
842      * @static
843      */
844     leftPad : function (val, size, ch) {
845         var result = new String(val);
846         if(ch === null || ch === undefined || ch === '') {
847             ch = " ";
848         }
849         while (result.length < size) {
850             result = ch + result;
851         }
852         return result;
853     },
854
855     /**
856      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
857      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
858      * <pre><code>
859 var cls = 'my-class', text = 'Some text';
860 var s = String.format('<div class="{0}">{1}</div>', cls, text);
861 // s now contains the string: '<div class="my-class">Some text</div>'
862 </code></pre>
863      * @param {String} string The tokenized string to be formatted
864      * @param {String} value1 The value to replace token {0}
865      * @param {String} value2 Etc...
866      * @return {String} The formatted string
867      * @static
868      */
869     format : function(format){
870         var args = Array.prototype.slice.call(arguments, 1);
871         return format.replace(/\{(\d+)\}/g, function(m, i){
872             return Roo.util.Format.htmlEncode(args[i]);
873         });
874     }
875 });
876
877 /**
878  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
879  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
880  * they are already different, the first value passed in is returned.  Note that this method returns the new value
881  * but does not change the current string.
882  * <pre><code>
883 // alternate sort directions
884 sort = sort.toggle('ASC', 'DESC');
885
886 // instead of conditional logic:
887 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
888 </code></pre>
889  * @param {String} value The value to compare to the current string
890  * @param {String} other The new value to use if the string already equals the first value passed in
891  * @return {String} The new value
892  */
893  
894 String.prototype.toggle = function(value, other){
895     return this == value ? other : value;
896 };/*
897  * Based on:
898  * Ext JS Library 1.1.1
899  * Copyright(c) 2006-2007, Ext JS, LLC.
900  *
901  * Originally Released Under LGPL - original licence link has changed is not relivant.
902  *
903  * Fork - LGPL
904  * <script type="text/javascript">
905  */
906
907  /**
908  * @class Number
909  */
910 Roo.applyIf(Number.prototype, {
911     /**
912      * Checks whether or not the current number is within a desired range.  If the number is already within the
913      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
914      * exceeded.  Note that this method returns the constrained value but does not change the current number.
915      * @param {Number} min The minimum number in the range
916      * @param {Number} max The maximum number in the range
917      * @return {Number} The constrained value if outside the range, otherwise the current value
918      */
919     constrain : function(min, max){
920         return Math.min(Math.max(this, min), max);
921     }
922 });/*
923  * Based on:
924  * Ext JS Library 1.1.1
925  * Copyright(c) 2006-2007, Ext JS, LLC.
926  *
927  * Originally Released Under LGPL - original licence link has changed is not relivant.
928  *
929  * Fork - LGPL
930  * <script type="text/javascript">
931  */
932  /**
933  * @class Array
934  */
935 Roo.applyIf(Array.prototype, {
936     /**
937      * 
938      * Checks whether or not the specified object exists in the array.
939      * @param {Object} o The object to check for
940      * @return {Number} The index of o in the array (or -1 if it is not found)
941      */
942     indexOf : function(o){
943        for (var i = 0, len = this.length; i < len; i++){
944               if(this[i] == o) { return i; }
945        }
946            return -1;
947     },
948
949     /**
950      * Removes the specified object from the array.  If the object is not found nothing happens.
951      * @param {Object} o The object to remove
952      */
953     remove : function(o){
954        var index = this.indexOf(o);
955        if(index != -1){
956            this.splice(index, 1);
957        }
958     },
959     /**
960      * Map (JS 1.6 compatibility)
961      * @param {Function} function  to call
962      */
963     map : function(fun )
964     {
965         var len = this.length >>> 0;
966         if (typeof fun != "function") {
967             throw new TypeError();
968         }
969         var res = new Array(len);
970         var thisp = arguments[1];
971         for (var i = 0; i < len; i++)
972         {
973             if (i in this) {
974                 res[i] = fun.call(thisp, this[i], i, this);
975             }
976         }
977
978         return res;
979     }
980     
981 });
982
983
984  
985 /*
986  * Based on:
987  * Ext JS Library 1.1.1
988  * Copyright(c) 2006-2007, Ext JS, LLC.
989  *
990  * Originally Released Under LGPL - original licence link has changed is not relivant.
991  *
992  * Fork - LGPL
993  * <script type="text/javascript">
994  */
995
996 /**
997  * @class Date
998  *
999  * The date parsing and format syntax is a subset of
1000  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1001  * supported will provide results equivalent to their PHP versions.
1002  *
1003  * Following is the list of all currently supported formats:
1004  *<pre>
1005 Sample date:
1006 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1007
1008 Format  Output      Description
1009 ------  ----------  --------------------------------------------------------------
1010   d      10         Day of the month, 2 digits with leading zeros
1011   D      Wed        A textual representation of a day, three letters
1012   j      10         Day of the month without leading zeros
1013   l      Wednesday  A full textual representation of the day of the week
1014   S      th         English ordinal day of month suffix, 2 chars (use with j)
1015   w      3          Numeric representation of the day of the week
1016   z      9          The julian date, or day of the year (0-365)
1017   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1018   F      January    A full textual representation of the month
1019   m      01         Numeric representation of a month, with leading zeros
1020   M      Jan        Month name abbreviation, three letters
1021   n      1          Numeric representation of a month, without leading zeros
1022   t      31         Number of days in the given month
1023   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1024   Y      2007       A full numeric representation of a year, 4 digits
1025   y      07         A two digit representation of a year
1026   a      pm         Lowercase Ante meridiem and Post meridiem
1027   A      PM         Uppercase Ante meridiem and Post meridiem
1028   g      3          12-hour format of an hour without leading zeros
1029   G      15         24-hour format of an hour without leading zeros
1030   h      03         12-hour format of an hour with leading zeros
1031   H      15         24-hour format of an hour with leading zeros
1032   i      05         Minutes with leading zeros
1033   s      01         Seconds, with leading zeros
1034   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1035   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1036   T      CST        Timezone setting of the machine running the code
1037   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1038 </pre>
1039  *
1040  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1041  * <pre><code>
1042 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1043 document.write(dt.format('Y-m-d'));                         //2007-01-10
1044 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1045 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
1046  </code></pre>
1047  *
1048  * Here are some standard date/time patterns that you might find helpful.  They
1049  * are not part of the source of Date.js, but to use them you can simply copy this
1050  * block of code into any script that is included after Date.js and they will also become
1051  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1052  * <pre><code>
1053 Date.patterns = {
1054     ISO8601Long:"Y-m-d H:i:s",
1055     ISO8601Short:"Y-m-d",
1056     ShortDate: "n/j/Y",
1057     LongDate: "l, F d, Y",
1058     FullDateTime: "l, F d, Y g:i:s A",
1059     MonthDay: "F d",
1060     ShortTime: "g:i A",
1061     LongTime: "g:i:s A",
1062     SortableDateTime: "Y-m-d\\TH:i:s",
1063     UniversalSortableDateTime: "Y-m-d H:i:sO",
1064     YearMonth: "F, Y"
1065 };
1066 </code></pre>
1067  *
1068  * Example usage:
1069  * <pre><code>
1070 var dt = new Date();
1071 document.write(dt.format(Date.patterns.ShortDate));
1072  </code></pre>
1073  */
1074
1075 /*
1076  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1077  * They generate precompiled functions from date formats instead of parsing and
1078  * processing the pattern every time you format a date.  These functions are available
1079  * on every Date object (any javascript function).
1080  *
1081  * The original article and download are here:
1082  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1083  *
1084  */
1085  
1086  
1087  // was in core
1088 /**
1089  Returns the number of milliseconds between this date and date
1090  @param {Date} date (optional) Defaults to now
1091  @return {Number} The diff in milliseconds
1092  @member Date getElapsed
1093  */
1094 Date.prototype.getElapsed = function(date) {
1095         return Math.abs((date || new Date()).getTime()-this.getTime());
1096 };
1097 // was in date file..
1098
1099
1100 // private
1101 Date.parseFunctions = {count:0};
1102 // private
1103 Date.parseRegexes = [];
1104 // private
1105 Date.formatFunctions = {count:0};
1106
1107 // private
1108 Date.prototype.dateFormat = function(format) {
1109     if (Date.formatFunctions[format] == null) {
1110         Date.createNewFormat(format);
1111     }
1112     var func = Date.formatFunctions[format];
1113     return this[func]();
1114 };
1115
1116
1117 /**
1118  * Formats a date given the supplied format string
1119  * @param {String} format The format string
1120  * @return {String} The formatted date
1121  * @method
1122  */
1123 Date.prototype.format = Date.prototype.dateFormat;
1124
1125 // private
1126 Date.createNewFormat = function(format) {
1127     var funcName = "format" + Date.formatFunctions.count++;
1128     Date.formatFunctions[format] = funcName;
1129     var code = "Date.prototype." + funcName + " = function(){return ";
1130     var special = false;
1131     var ch = '';
1132     for (var i = 0; i < format.length; ++i) {
1133         ch = format.charAt(i);
1134         if (!special && ch == "\\") {
1135             special = true;
1136         }
1137         else if (special) {
1138             special = false;
1139             code += "'" + String.escape(ch) + "' + ";
1140         }
1141         else {
1142             code += Date.getFormatCode(ch);
1143         }
1144     }
1145     /** eval:var:zzzzzzzzzzzzz */
1146     eval(code.substring(0, code.length - 3) + ";}");
1147 };
1148
1149 // private
1150 Date.getFormatCode = function(character) {
1151     switch (character) {
1152     case "d":
1153         return "String.leftPad(this.getDate(), 2, '0') + ";
1154     case "D":
1155         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1156     case "j":
1157         return "this.getDate() + ";
1158     case "l":
1159         return "Date.dayNames[this.getDay()] + ";
1160     case "S":
1161         return "this.getSuffix() + ";
1162     case "w":
1163         return "this.getDay() + ";
1164     case "z":
1165         return "this.getDayOfYear() + ";
1166     case "W":
1167         return "this.getWeekOfYear() + ";
1168     case "F":
1169         return "Date.monthNames[this.getMonth()] + ";
1170     case "m":
1171         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1172     case "M":
1173         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1174     case "n":
1175         return "(this.getMonth() + 1) + ";
1176     case "t":
1177         return "this.getDaysInMonth() + ";
1178     case "L":
1179         return "(this.isLeapYear() ? 1 : 0) + ";
1180     case "Y":
1181         return "this.getFullYear() + ";
1182     case "y":
1183         return "('' + this.getFullYear()).substring(2, 4) + ";
1184     case "a":
1185         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1186     case "A":
1187         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1188     case "g":
1189         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1190     case "G":
1191         return "this.getHours() + ";
1192     case "h":
1193         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1194     case "H":
1195         return "String.leftPad(this.getHours(), 2, '0') + ";
1196     case "i":
1197         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1198     case "s":
1199         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1200     case "O":
1201         return "this.getGMTOffset() + ";
1202     case "P":
1203         return "this.getGMTColonOffset() + ";
1204     case "T":
1205         return "this.getTimezone() + ";
1206     case "Z":
1207         return "(this.getTimezoneOffset() * -60) + ";
1208     default:
1209         return "'" + String.escape(character) + "' + ";
1210     }
1211 };
1212
1213 /**
1214  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1215  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1216  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1217  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1218  * string or the parse operation will fail.
1219  * Example Usage:
1220 <pre><code>
1221 //dt = Fri May 25 2007 (current date)
1222 var dt = new Date();
1223
1224 //dt = Thu May 25 2006 (today's month/day in 2006)
1225 dt = Date.parseDate("2006", "Y");
1226
1227 //dt = Sun Jan 15 2006 (all date parts specified)
1228 dt = Date.parseDate("2006-1-15", "Y-m-d");
1229
1230 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1231 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1232 </code></pre>
1233  * @param {String} input The unparsed date as a string
1234  * @param {String} format The format the date is in
1235  * @return {Date} The parsed date
1236  * @static
1237  */
1238 Date.parseDate = function(input, format) {
1239     if (Date.parseFunctions[format] == null) {
1240         Date.createParser(format);
1241     }
1242     var func = Date.parseFunctions[format];
1243     return Date[func](input);
1244 };
1245 /**
1246  * @private
1247  */
1248
1249 Date.createParser = function(format) {
1250     var funcName = "parse" + Date.parseFunctions.count++;
1251     var regexNum = Date.parseRegexes.length;
1252     var currentGroup = 1;
1253     Date.parseFunctions[format] = funcName;
1254
1255     var code = "Date." + funcName + " = function(input){\n"
1256         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1257         + "var d = new Date();\n"
1258         + "y = d.getFullYear();\n"
1259         + "m = d.getMonth();\n"
1260         + "d = d.getDate();\n"
1261         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1262         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1263         + "if (results && results.length > 0) {";
1264     var regex = "";
1265
1266     var special = false;
1267     var ch = '';
1268     for (var i = 0; i < format.length; ++i) {
1269         ch = format.charAt(i);
1270         if (!special && ch == "\\") {
1271             special = true;
1272         }
1273         else if (special) {
1274             special = false;
1275             regex += String.escape(ch);
1276         }
1277         else {
1278             var obj = Date.formatCodeToRegex(ch, currentGroup);
1279             currentGroup += obj.g;
1280             regex += obj.s;
1281             if (obj.g && obj.c) {
1282                 code += obj.c;
1283             }
1284         }
1285     }
1286
1287     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1288         + "{v = new Date(y, m, d, h, i, s);}\n"
1289         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1290         + "{v = new Date(y, m, d, h, i);}\n"
1291         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1292         + "{v = new Date(y, m, d, h);}\n"
1293         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1294         + "{v = new Date(y, m, d);}\n"
1295         + "else if (y >= 0 && m >= 0)\n"
1296         + "{v = new Date(y, m);}\n"
1297         + "else if (y >= 0)\n"
1298         + "{v = new Date(y);}\n"
1299         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1300         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1301         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1302         + ";}";
1303
1304     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1305     /** eval:var:zzzzzzzzzzzzz */
1306     eval(code);
1307 };
1308
1309 // private
1310 Date.formatCodeToRegex = function(character, currentGroup) {
1311     switch (character) {
1312     case "D":
1313         return {g:0,
1314         c:null,
1315         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1316     case "j":
1317         return {g:1,
1318             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1319             s:"(\\d{1,2})"}; // day of month without leading zeroes
1320     case "d":
1321         return {g:1,
1322             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1323             s:"(\\d{2})"}; // day of month with leading zeroes
1324     case "l":
1325         return {g:0,
1326             c:null,
1327             s:"(?:" + Date.dayNames.join("|") + ")"};
1328     case "S":
1329         return {g:0,
1330             c:null,
1331             s:"(?:st|nd|rd|th)"};
1332     case "w":
1333         return {g:0,
1334             c:null,
1335             s:"\\d"};
1336     case "z":
1337         return {g:0,
1338             c:null,
1339             s:"(?:\\d{1,3})"};
1340     case "W":
1341         return {g:0,
1342             c:null,
1343             s:"(?:\\d{2})"};
1344     case "F":
1345         return {g:1,
1346             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1347             s:"(" + Date.monthNames.join("|") + ")"};
1348     case "M":
1349         return {g:1,
1350             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1351             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1352     case "n":
1353         return {g:1,
1354             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1355             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1356     case "m":
1357         return {g:1,
1358             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1359             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1360     case "t":
1361         return {g:0,
1362             c:null,
1363             s:"\\d{1,2}"};
1364     case "L":
1365         return {g:0,
1366             c:null,
1367             s:"(?:1|0)"};
1368     case "Y":
1369         return {g:1,
1370             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{4})"};
1372     case "y":
1373         return {g:1,
1374             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1375                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1376             s:"(\\d{1,2})"};
1377     case "a":
1378         return {g:1,
1379             c:"if (results[" + currentGroup + "] == 'am') {\n"
1380                 + "if (h == 12) { h = 0; }\n"
1381                 + "} else { if (h < 12) { h += 12; }}",
1382             s:"(am|pm)"};
1383     case "A":
1384         return {g:1,
1385             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1386                 + "if (h == 12) { h = 0; }\n"
1387                 + "} else { if (h < 12) { h += 12; }}",
1388             s:"(AM|PM)"};
1389     case "g":
1390     case "G":
1391         return {g:1,
1392             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1393             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1394     case "h":
1395     case "H":
1396         return {g:1,
1397             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1398             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1399     case "i":
1400         return {g:1,
1401             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1402             s:"(\\d{2})"};
1403     case "s":
1404         return {g:1,
1405             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1406             s:"(\\d{2})"};
1407     case "O":
1408         return {g:1,
1409             c:[
1410                 "o = results[", currentGroup, "];\n",
1411                 "var sn = o.substring(0,1);\n", // get + / - sign
1412                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1413                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1414                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1415                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1416             ].join(""),
1417             s:"([+\-]\\d{2,4})"};
1418     
1419     
1420     case "P":
1421         return {g:1,
1422                 c:[
1423                    "o = results[", currentGroup, "];\n",
1424                    "var sn = o.substring(0,1);\n",
1425                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1426                    "var mn = o.substring(4,6) % 60;\n",
1427                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1428                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1429             ].join(""),
1430             s:"([+\-]\\d{4})"};
1431     case "T":
1432         return {g:0,
1433             c:null,
1434             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1435     case "Z":
1436         return {g:1,
1437             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1438                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1439             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1440     default:
1441         return {g:0,
1442             c:null,
1443             s:String.escape(character)};
1444     }
1445 };
1446
1447 /**
1448  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1449  * @return {String} The abbreviated timezone name (e.g. 'CST')
1450  */
1451 Date.prototype.getTimezone = function() {
1452     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1453 };
1454
1455 /**
1456  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1457  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1458  */
1459 Date.prototype.getGMTOffset = function() {
1460     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1461         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1462         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1463 };
1464
1465 /**
1466  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1467  * @return {String} 2-characters representing hours and 2-characters representing minutes
1468  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1469  */
1470 Date.prototype.getGMTColonOffset = function() {
1471         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1472                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1473                 + ":"
1474                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1475 }
1476
1477 /**
1478  * Get the numeric day number of the year, adjusted for leap year.
1479  * @return {Number} 0 through 364 (365 in leap years)
1480  */
1481 Date.prototype.getDayOfYear = function() {
1482     var num = 0;
1483     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1484     for (var i = 0; i < this.getMonth(); ++i) {
1485         num += Date.daysInMonth[i];
1486     }
1487     return num + this.getDate() - 1;
1488 };
1489
1490 /**
1491  * Get the string representation of the numeric week number of the year
1492  * (equivalent to the format specifier 'W').
1493  * @return {String} '00' through '52'
1494  */
1495 Date.prototype.getWeekOfYear = function() {
1496     // Skip to Thursday of this week
1497     var now = this.getDayOfYear() + (4 - this.getDay());
1498     // Find the first Thursday of the year
1499     var jan1 = new Date(this.getFullYear(), 0, 1);
1500     var then = (7 - jan1.getDay() + 4);
1501     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1502 };
1503
1504 /**
1505  * Whether or not the current date is in a leap year.
1506  * @return {Boolean} True if the current date is in a leap year, else false
1507  */
1508 Date.prototype.isLeapYear = function() {
1509     var year = this.getFullYear();
1510     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1511 };
1512
1513 /**
1514  * Get the first day of the current month, adjusted for leap year.  The returned value
1515  * is the numeric day index within the week (0-6) which can be used in conjunction with
1516  * the {@link #monthNames} array to retrieve the textual day name.
1517  * Example:
1518  *<pre><code>
1519 var dt = new Date('1/10/2007');
1520 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1521 </code></pre>
1522  * @return {Number} The day number (0-6)
1523  */
1524 Date.prototype.getFirstDayOfMonth = function() {
1525     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1526     return (day < 0) ? (day + 7) : day;
1527 };
1528
1529 /**
1530  * Get the last day of the current month, adjusted for leap year.  The returned value
1531  * is the numeric day index within the week (0-6) which can be used in conjunction with
1532  * the {@link #monthNames} array to retrieve the textual day name.
1533  * Example:
1534  *<pre><code>
1535 var dt = new Date('1/10/2007');
1536 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1537 </code></pre>
1538  * @return {Number} The day number (0-6)
1539  */
1540 Date.prototype.getLastDayOfMonth = function() {
1541     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1542     return (day < 0) ? (day + 7) : day;
1543 };
1544
1545
1546 /**
1547  * Get the first date of this date's month
1548  * @return {Date}
1549  */
1550 Date.prototype.getFirstDateOfMonth = function() {
1551     return new Date(this.getFullYear(), this.getMonth(), 1);
1552 };
1553
1554 /**
1555  * Get the last date of this date's month
1556  * @return {Date}
1557  */
1558 Date.prototype.getLastDateOfMonth = function() {
1559     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1560 };
1561 /**
1562  * Get the number of days in the current month, adjusted for leap year.
1563  * @return {Number} The number of days in the month
1564  */
1565 Date.prototype.getDaysInMonth = function() {
1566     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1567     return Date.daysInMonth[this.getMonth()];
1568 };
1569
1570 /**
1571  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1572  * @return {String} 'st, 'nd', 'rd' or 'th'
1573  */
1574 Date.prototype.getSuffix = function() {
1575     switch (this.getDate()) {
1576         case 1:
1577         case 21:
1578         case 31:
1579             return "st";
1580         case 2:
1581         case 22:
1582             return "nd";
1583         case 3:
1584         case 23:
1585             return "rd";
1586         default:
1587             return "th";
1588     }
1589 };
1590
1591 // private
1592 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1593
1594 /**
1595  * An array of textual month names.
1596  * Override these values for international dates, for example...
1597  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1598  * @type Array
1599  * @static
1600  */
1601 Date.monthNames =
1602    ["January",
1603     "February",
1604     "March",
1605     "April",
1606     "May",
1607     "June",
1608     "July",
1609     "August",
1610     "September",
1611     "October",
1612     "November",
1613     "December"];
1614
1615 /**
1616  * An array of textual day names.
1617  * Override these values for international dates, for example...
1618  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1619  * @type Array
1620  * @static
1621  */
1622 Date.dayNames =
1623    ["Sunday",
1624     "Monday",
1625     "Tuesday",
1626     "Wednesday",
1627     "Thursday",
1628     "Friday",
1629     "Saturday"];
1630
1631 // private
1632 Date.y2kYear = 50;
1633 // private
1634 Date.monthNumbers = {
1635     Jan:0,
1636     Feb:1,
1637     Mar:2,
1638     Apr:3,
1639     May:4,
1640     Jun:5,
1641     Jul:6,
1642     Aug:7,
1643     Sep:8,
1644     Oct:9,
1645     Nov:10,
1646     Dec:11};
1647
1648 /**
1649  * Creates and returns a new Date instance with the exact same date value as the called instance.
1650  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1651  * variable will also be changed.  When the intention is to create a new variable that will not
1652  * modify the original instance, you should create a clone.
1653  *
1654  * Example of correctly cloning a date:
1655  * <pre><code>
1656 //wrong way:
1657 var orig = new Date('10/1/2006');
1658 var copy = orig;
1659 copy.setDate(5);
1660 document.write(orig);  //returns 'Thu Oct 05 2006'!
1661
1662 //correct way:
1663 var orig = new Date('10/1/2006');
1664 var copy = orig.clone();
1665 copy.setDate(5);
1666 document.write(orig);  //returns 'Thu Oct 01 2006'
1667 </code></pre>
1668  * @return {Date} The new Date instance
1669  */
1670 Date.prototype.clone = function() {
1671         return new Date(this.getTime());
1672 };
1673
1674 /**
1675  * Clears any time information from this date
1676  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1677  @return {Date} this or the clone
1678  */
1679 Date.prototype.clearTime = function(clone){
1680     if(clone){
1681         return this.clone().clearTime();
1682     }
1683     this.setHours(0);
1684     this.setMinutes(0);
1685     this.setSeconds(0);
1686     this.setMilliseconds(0);
1687     return this;
1688 };
1689
1690 // private
1691 // safari setMonth is broken
1692 if(Roo.isSafari){
1693     Date.brokenSetMonth = Date.prototype.setMonth;
1694         Date.prototype.setMonth = function(num){
1695                 if(num <= -1){
1696                         var n = Math.ceil(-num);
1697                         var back_year = Math.ceil(n/12);
1698                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1699                         this.setFullYear(this.getFullYear() - back_year);
1700                         return Date.brokenSetMonth.call(this, month);
1701                 } else {
1702                         return Date.brokenSetMonth.apply(this, arguments);
1703                 }
1704         };
1705 }
1706
1707 /** Date interval constant 
1708 * @static 
1709 * @type String */
1710 Date.MILLI = "ms";
1711 /** Date interval constant 
1712 * @static 
1713 * @type String */
1714 Date.SECOND = "s";
1715 /** Date interval constant 
1716 * @static 
1717 * @type String */
1718 Date.MINUTE = "mi";
1719 /** Date interval constant 
1720 * @static 
1721 * @type String */
1722 Date.HOUR = "h";
1723 /** Date interval constant 
1724 * @static 
1725 * @type String */
1726 Date.DAY = "d";
1727 /** Date interval constant 
1728 * @static 
1729 * @type String */
1730 Date.MONTH = "mo";
1731 /** Date interval constant 
1732 * @static 
1733 * @type String */
1734 Date.YEAR = "y";
1735
1736 /**
1737  * Provides a convenient method of performing basic date arithmetic.  This method
1738  * does not modify the Date instance being called - it creates and returns
1739  * a new Date instance containing the resulting date value.
1740  *
1741  * Examples:
1742  * <pre><code>
1743 //Basic usage:
1744 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1745 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1746
1747 //Negative values will subtract correctly:
1748 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1749 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1750
1751 //You can even chain several calls together in one line!
1752 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1753 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1754  </code></pre>
1755  *
1756  * @param {String} interval   A valid date interval enum value
1757  * @param {Number} value      The amount to add to the current date
1758  * @return {Date} The new Date instance
1759  */
1760 Date.prototype.add = function(interval, value){
1761   var d = this.clone();
1762   if (!interval || value === 0) { return d; }
1763   switch(interval.toLowerCase()){
1764     case Date.MILLI:
1765       d.setMilliseconds(this.getMilliseconds() + value);
1766       break;
1767     case Date.SECOND:
1768       d.setSeconds(this.getSeconds() + value);
1769       break;
1770     case Date.MINUTE:
1771       d.setMinutes(this.getMinutes() + value);
1772       break;
1773     case Date.HOUR:
1774       d.setHours(this.getHours() + value);
1775       break;
1776     case Date.DAY:
1777       d.setDate(this.getDate() + value);
1778       break;
1779     case Date.MONTH:
1780       var day = this.getDate();
1781       if(day > 28){
1782           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1783       }
1784       d.setDate(day);
1785       d.setMonth(this.getMonth() + value);
1786       break;
1787     case Date.YEAR:
1788       d.setFullYear(this.getFullYear() + value);
1789       break;
1790   }
1791   return d;
1792 };
1793 /*
1794  * Based on:
1795  * Ext JS Library 1.1.1
1796  * Copyright(c) 2006-2007, Ext JS, LLC.
1797  *
1798  * Originally Released Under LGPL - original licence link has changed is not relivant.
1799  *
1800  * Fork - LGPL
1801  * <script type="text/javascript">
1802  */
1803
1804 /**
1805  * @class Roo.lib.Dom
1806  * @static
1807  * 
1808  * Dom utils (from YIU afaik)
1809  * 
1810  **/
1811 Roo.lib.Dom = {
1812     /**
1813      * Get the view width
1814      * @param {Boolean} full True will get the full document, otherwise it's the view width
1815      * @return {Number} The width
1816      */
1817      
1818     getViewWidth : function(full) {
1819         return full ? this.getDocumentWidth() : this.getViewportWidth();
1820     },
1821     /**
1822      * Get the view height
1823      * @param {Boolean} full True will get the full document, otherwise it's the view height
1824      * @return {Number} The height
1825      */
1826     getViewHeight : function(full) {
1827         return full ? this.getDocumentHeight() : this.getViewportHeight();
1828     },
1829
1830     getDocumentHeight: function() {
1831         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1832         return Math.max(scrollHeight, this.getViewportHeight());
1833     },
1834
1835     getDocumentWidth: function() {
1836         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1837         return Math.max(scrollWidth, this.getViewportWidth());
1838     },
1839
1840     getViewportHeight: function() {
1841         var height = self.innerHeight;
1842         var mode = document.compatMode;
1843
1844         if ((mode || Roo.isIE) && !Roo.isOpera) {
1845             height = (mode == "CSS1Compat") ?
1846                      document.documentElement.clientHeight :
1847                      document.body.clientHeight;
1848         }
1849
1850         return height;
1851     },
1852
1853     getViewportWidth: function() {
1854         var width = self.innerWidth;
1855         var mode = document.compatMode;
1856
1857         if (mode || Roo.isIE) {
1858             width = (mode == "CSS1Compat") ?
1859                     document.documentElement.clientWidth :
1860                     document.body.clientWidth;
1861         }
1862         return width;
1863     },
1864
1865     isAncestor : function(p, c) {
1866         p = Roo.getDom(p);
1867         c = Roo.getDom(c);
1868         if (!p || !c) {
1869             return false;
1870         }
1871
1872         if (p.contains && !Roo.isSafari) {
1873             return p.contains(c);
1874         } else if (p.compareDocumentPosition) {
1875             return !!(p.compareDocumentPosition(c) & 16);
1876         } else {
1877             var parent = c.parentNode;
1878             while (parent) {
1879                 if (parent == p) {
1880                     return true;
1881                 }
1882                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1883                     return false;
1884                 }
1885                 parent = parent.parentNode;
1886             }
1887             return false;
1888         }
1889     },
1890
1891     getRegion : function(el) {
1892         return Roo.lib.Region.getRegion(el);
1893     },
1894
1895     getY : function(el) {
1896         return this.getXY(el)[1];
1897     },
1898
1899     getX : function(el) {
1900         return this.getXY(el)[0];
1901     },
1902
1903     getXY : function(el) {
1904         var p, pe, b, scroll, bd = document.body;
1905         el = Roo.getDom(el);
1906         var fly = Roo.lib.AnimBase.fly;
1907         if (el.getBoundingClientRect) {
1908             b = el.getBoundingClientRect();
1909             scroll = fly(document).getScroll();
1910             return [b.left + scroll.left, b.top + scroll.top];
1911         }
1912         var x = 0, y = 0;
1913
1914         p = el;
1915
1916         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1917
1918         while (p) {
1919
1920             x += p.offsetLeft;
1921             y += p.offsetTop;
1922
1923             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1924                 hasAbsolute = true;
1925             }
1926
1927             if (Roo.isGecko) {
1928                 pe = fly(p);
1929
1930                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1931                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1932
1933
1934                 x += bl;
1935                 y += bt;
1936
1937
1938                 if (p != el && pe.getStyle('overflow') != 'visible') {
1939                     x += bl;
1940                     y += bt;
1941                 }
1942             }
1943             p = p.offsetParent;
1944         }
1945
1946         if (Roo.isSafari && hasAbsolute) {
1947             x -= bd.offsetLeft;
1948             y -= bd.offsetTop;
1949         }
1950
1951         if (Roo.isGecko && !hasAbsolute) {
1952             var dbd = fly(bd);
1953             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1954             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1955         }
1956
1957         p = el.parentNode;
1958         while (p && p != bd) {
1959             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1960                 x -= p.scrollLeft;
1961                 y -= p.scrollTop;
1962             }
1963             p = p.parentNode;
1964         }
1965         return [x, y];
1966     },
1967  
1968   
1969
1970
1971     setXY : function(el, xy) {
1972         el = Roo.fly(el, '_setXY');
1973         el.position();
1974         var pts = el.translatePoints(xy);
1975         if (xy[0] !== false) {
1976             el.dom.style.left = pts.left + "px";
1977         }
1978         if (xy[1] !== false) {
1979             el.dom.style.top = pts.top + "px";
1980         }
1981     },
1982
1983     setX : function(el, x) {
1984         this.setXY(el, [x, false]);
1985     },
1986
1987     setY : function(el, y) {
1988         this.setXY(el, [false, y]);
1989     }
1990 };
1991 /*
1992  * Portions of this file are based on pieces of Yahoo User Interface Library
1993  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1994  * YUI licensed under the BSD License:
1995  * http://developer.yahoo.net/yui/license.txt
1996  * <script type="text/javascript">
1997  *
1998  */
1999
2000 Roo.lib.Event = function() {
2001     var loadComplete = false;
2002     var listeners = [];
2003     var unloadListeners = [];
2004     var retryCount = 0;
2005     var onAvailStack = [];
2006     var counter = 0;
2007     var lastError = null;
2008
2009     return {
2010         POLL_RETRYS: 200,
2011         POLL_INTERVAL: 20,
2012         EL: 0,
2013         TYPE: 1,
2014         FN: 2,
2015         WFN: 3,
2016         OBJ: 3,
2017         ADJ_SCOPE: 4,
2018         _interval: null,
2019
2020         startInterval: function() {
2021             if (!this._interval) {
2022                 var self = this;
2023                 var callback = function() {
2024                     self._tryPreloadAttach();
2025                 };
2026                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2027
2028             }
2029         },
2030
2031         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2032             onAvailStack.push({ id:         p_id,
2033                 fn:         p_fn,
2034                 obj:        p_obj,
2035                 override:   p_override,
2036                 checkReady: false    });
2037
2038             retryCount = this.POLL_RETRYS;
2039             this.startInterval();
2040         },
2041
2042
2043         addListener: function(el, eventName, fn) {
2044             el = Roo.getDom(el);
2045             if (!el || !fn) {
2046                 return false;
2047             }
2048
2049             if ("unload" == eventName) {
2050                 unloadListeners[unloadListeners.length] =
2051                 [el, eventName, fn];
2052                 return true;
2053             }
2054
2055             var wrappedFn = function(e) {
2056                 return fn(Roo.lib.Event.getEvent(e));
2057             };
2058
2059             var li = [el, eventName, fn, wrappedFn];
2060
2061             var index = listeners.length;
2062             listeners[index] = li;
2063
2064             this.doAdd(el, eventName, wrappedFn, false);
2065             return true;
2066
2067         },
2068
2069
2070         removeListener: function(el, eventName, fn) {
2071             var i, len;
2072
2073             el = Roo.getDom(el);
2074
2075             if(!fn) {
2076                 return this.purgeElement(el, false, eventName);
2077             }
2078
2079
2080             if ("unload" == eventName) {
2081
2082                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2083                     var li = unloadListeners[i];
2084                     if (li &&
2085                         li[0] == el &&
2086                         li[1] == eventName &&
2087                         li[2] == fn) {
2088                         unloadListeners.splice(i, 1);
2089                         return true;
2090                     }
2091                 }
2092
2093                 return false;
2094             }
2095
2096             var cacheItem = null;
2097
2098
2099             var index = arguments[3];
2100
2101             if ("undefined" == typeof index) {
2102                 index = this._getCacheIndex(el, eventName, fn);
2103             }
2104
2105             if (index >= 0) {
2106                 cacheItem = listeners[index];
2107             }
2108
2109             if (!el || !cacheItem) {
2110                 return false;
2111             }
2112
2113             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2114
2115             delete listeners[index][this.WFN];
2116             delete listeners[index][this.FN];
2117             listeners.splice(index, 1);
2118
2119             return true;
2120
2121         },
2122
2123
2124         getTarget: function(ev, resolveTextNode) {
2125             ev = ev.browserEvent || ev;
2126             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2127             var t = ev.target || ev.srcElement;
2128             return this.resolveTextNode(t);
2129         },
2130
2131
2132         resolveTextNode: function(node) {
2133             if (Roo.isSafari && node && 3 == node.nodeType) {
2134                 return node.parentNode;
2135             } else {
2136                 return node;
2137             }
2138         },
2139
2140
2141         getPageX: function(ev) {
2142             ev = ev.browserEvent || ev;
2143             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2144             var x = ev.pageX;
2145             if (!x && 0 !== x) {
2146                 x = ev.clientX || 0;
2147
2148                 if (Roo.isIE) {
2149                     x += this.getScroll()[1];
2150                 }
2151             }
2152
2153             return x;
2154         },
2155
2156
2157         getPageY: function(ev) {
2158             ev = ev.browserEvent || ev;
2159             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2160             var y = ev.pageY;
2161             if (!y && 0 !== y) {
2162                 y = ev.clientY || 0;
2163
2164                 if (Roo.isIE) {
2165                     y += this.getScroll()[0];
2166                 }
2167             }
2168
2169
2170             return y;
2171         },
2172
2173
2174         getXY: function(ev) {
2175             ev = ev.browserEvent || ev;
2176             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2177             return [this.getPageX(ev), this.getPageY(ev)];
2178         },
2179
2180
2181         getRelatedTarget: function(ev) {
2182             ev = ev.browserEvent || ev;
2183             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2184             var t = ev.relatedTarget;
2185             if (!t) {
2186                 if (ev.type == "mouseout") {
2187                     t = ev.toElement;
2188                 } else if (ev.type == "mouseover") {
2189                     t = ev.fromElement;
2190                 }
2191             }
2192
2193             return this.resolveTextNode(t);
2194         },
2195
2196
2197         getTime: function(ev) {
2198             ev = ev.browserEvent || ev;
2199             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2200             if (!ev.time) {
2201                 var t = new Date().getTime();
2202                 try {
2203                     ev.time = t;
2204                 } catch(ex) {
2205                     this.lastError = ex;
2206                     return t;
2207                 }
2208             }
2209
2210             return ev.time;
2211         },
2212
2213
2214         stopEvent: function(ev) {
2215             this.stopPropagation(ev);
2216             this.preventDefault(ev);
2217         },
2218
2219
2220         stopPropagation: function(ev) {
2221             ev = ev.browserEvent || ev;
2222             if (ev.stopPropagation) {
2223                 ev.stopPropagation();
2224             } else {
2225                 ev.cancelBubble = true;
2226             }
2227         },
2228
2229
2230         preventDefault: function(ev) {
2231             ev = ev.browserEvent || ev;
2232             if(ev.preventDefault) {
2233                 ev.preventDefault();
2234             } else {
2235                 ev.returnValue = false;
2236             }
2237         },
2238
2239
2240         getEvent: function(e) {
2241             var ev = e || window.event;
2242             if (!ev) {
2243                 var c = this.getEvent.caller;
2244                 while (c) {
2245                     ev = c.arguments[0];
2246                     if (ev && Event == ev.constructor) {
2247                         break;
2248                     }
2249                     c = c.caller;
2250                 }
2251             }
2252             return ev;
2253         },
2254
2255
2256         getCharCode: function(ev) {
2257             ev = ev.browserEvent || ev;
2258             return ev.charCode || ev.keyCode || 0;
2259         },
2260
2261
2262         _getCacheIndex: function(el, eventName, fn) {
2263             for (var i = 0,len = listeners.length; i < len; ++i) {
2264                 var li = listeners[i];
2265                 if (li &&
2266                     li[this.FN] == fn &&
2267                     li[this.EL] == el &&
2268                     li[this.TYPE] == eventName) {
2269                     return i;
2270                 }
2271             }
2272
2273             return -1;
2274         },
2275
2276
2277         elCache: {},
2278
2279
2280         getEl: function(id) {
2281             return document.getElementById(id);
2282         },
2283
2284
2285         clearCache: function() {
2286         },
2287
2288
2289         _load: function(e) {
2290             loadComplete = true;
2291             var EU = Roo.lib.Event;
2292
2293
2294             if (Roo.isIE) {
2295                 EU.doRemove(window, "load", EU._load);
2296             }
2297         },
2298
2299
2300         _tryPreloadAttach: function() {
2301
2302             if (this.locked) {
2303                 return false;
2304             }
2305
2306             this.locked = true;
2307
2308
2309             var tryAgain = !loadComplete;
2310             if (!tryAgain) {
2311                 tryAgain = (retryCount > 0);
2312             }
2313
2314
2315             var notAvail = [];
2316             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2317                 var item = onAvailStack[i];
2318                 if (item) {
2319                     var el = this.getEl(item.id);
2320
2321                     if (el) {
2322                         if (!item.checkReady ||
2323                             loadComplete ||
2324                             el.nextSibling ||
2325                             (document && document.body)) {
2326
2327                             var scope = el;
2328                             if (item.override) {
2329                                 if (item.override === true) {
2330                                     scope = item.obj;
2331                                 } else {
2332                                     scope = item.override;
2333                                 }
2334                             }
2335                             item.fn.call(scope, item.obj);
2336                             onAvailStack[i] = null;
2337                         }
2338                     } else {
2339                         notAvail.push(item);
2340                     }
2341                 }
2342             }
2343
2344             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2345
2346             if (tryAgain) {
2347
2348                 this.startInterval();
2349             } else {
2350                 clearInterval(this._interval);
2351                 this._interval = null;
2352             }
2353
2354             this.locked = false;
2355
2356             return true;
2357
2358         },
2359
2360
2361         purgeElement: function(el, recurse, eventName) {
2362             var elListeners = this.getListeners(el, eventName);
2363             if (elListeners) {
2364                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2365                     var l = elListeners[i];
2366                     this.removeListener(el, l.type, l.fn);
2367                 }
2368             }
2369
2370             if (recurse && el && el.childNodes) {
2371                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2372                     this.purgeElement(el.childNodes[i], recurse, eventName);
2373                 }
2374             }
2375         },
2376
2377
2378         getListeners: function(el, eventName) {
2379             var results = [], searchLists;
2380             if (!eventName) {
2381                 searchLists = [listeners, unloadListeners];
2382             } else if (eventName == "unload") {
2383                 searchLists = [unloadListeners];
2384             } else {
2385                 searchLists = [listeners];
2386             }
2387
2388             for (var j = 0; j < searchLists.length; ++j) {
2389                 var searchList = searchLists[j];
2390                 if (searchList && searchList.length > 0) {
2391                     for (var i = 0,len = searchList.length; i < len; ++i) {
2392                         var l = searchList[i];
2393                         if (l && l[this.EL] === el &&
2394                             (!eventName || eventName === l[this.TYPE])) {
2395                             results.push({
2396                                 type:   l[this.TYPE],
2397                                 fn:     l[this.FN],
2398                                 obj:    l[this.OBJ],
2399                                 adjust: l[this.ADJ_SCOPE],
2400                                 index:  i
2401                             });
2402                         }
2403                     }
2404                 }
2405             }
2406
2407             return (results.length) ? results : null;
2408         },
2409
2410
2411         _unload: function(e) {
2412
2413             var EU = Roo.lib.Event, i, j, l, len, index;
2414
2415             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2416                 l = unloadListeners[i];
2417                 if (l) {
2418                     var scope = window;
2419                     if (l[EU.ADJ_SCOPE]) {
2420                         if (l[EU.ADJ_SCOPE] === true) {
2421                             scope = l[EU.OBJ];
2422                         } else {
2423                             scope = l[EU.ADJ_SCOPE];
2424                         }
2425                     }
2426                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2427                     unloadListeners[i] = null;
2428                     l = null;
2429                     scope = null;
2430                 }
2431             }
2432
2433             unloadListeners = null;
2434
2435             if (listeners && listeners.length > 0) {
2436                 j = listeners.length;
2437                 while (j) {
2438                     index = j - 1;
2439                     l = listeners[index];
2440                     if (l) {
2441                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2442                                 l[EU.FN], index);
2443                     }
2444                     j = j - 1;
2445                 }
2446                 l = null;
2447
2448                 EU.clearCache();
2449             }
2450
2451             EU.doRemove(window, "unload", EU._unload);
2452
2453         },
2454
2455
2456         getScroll: function() {
2457             var dd = document.documentElement, db = document.body;
2458             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2459                 return [dd.scrollTop, dd.scrollLeft];
2460             } else if (db) {
2461                 return [db.scrollTop, db.scrollLeft];
2462             } else {
2463                 return [0, 0];
2464             }
2465         },
2466
2467
2468         doAdd: function () {
2469             if (window.addEventListener) {
2470                 return function(el, eventName, fn, capture) {
2471                     el.addEventListener(eventName, fn, (capture));
2472                 };
2473             } else if (window.attachEvent) {
2474                 return function(el, eventName, fn, capture) {
2475                     el.attachEvent("on" + eventName, fn);
2476                 };
2477             } else {
2478                 return function() {
2479                 };
2480             }
2481         }(),
2482
2483
2484         doRemove: function() {
2485             if (window.removeEventListener) {
2486                 return function (el, eventName, fn, capture) {
2487                     el.removeEventListener(eventName, fn, (capture));
2488                 };
2489             } else if (window.detachEvent) {
2490                 return function (el, eventName, fn) {
2491                     el.detachEvent("on" + eventName, fn);
2492                 };
2493             } else {
2494                 return function() {
2495                 };
2496             }
2497         }()
2498     };
2499     
2500 }();
2501 (function() {     
2502    
2503     var E = Roo.lib.Event;
2504     E.on = E.addListener;
2505     E.un = E.removeListener;
2506
2507     if (document && document.body) {
2508         E._load();
2509     } else {
2510         E.doAdd(window, "load", E._load);
2511     }
2512     E.doAdd(window, "unload", E._unload);
2513     E._tryPreloadAttach();
2514 })();
2515
2516 /*
2517  * Portions of this file are based on pieces of Yahoo User Interface Library
2518  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2519  * YUI licensed under the BSD License:
2520  * http://developer.yahoo.net/yui/license.txt
2521  * <script type="text/javascript">
2522  *
2523  */
2524
2525 (function() {
2526     /**
2527      * @class Roo.lib.Ajax
2528      *
2529      */
2530     Roo.lib.Ajax = {
2531         /**
2532          * @static 
2533          */
2534         request : function(method, uri, cb, data, options) {
2535             if(options){
2536                 var hs = options.headers;
2537                 if(hs){
2538                     for(var h in hs){
2539                         if(hs.hasOwnProperty(h)){
2540                             this.initHeader(h, hs[h], false);
2541                         }
2542                     }
2543                 }
2544                 if(options.xmlData){
2545                     this.initHeader('Content-Type', 'text/xml', false);
2546                     method = 'POST';
2547                     data = options.xmlData;
2548                 }
2549             }
2550
2551             return this.asyncRequest(method, uri, cb, data);
2552         },
2553
2554         serializeForm : function(form) {
2555             if(typeof form == 'string') {
2556                 form = (document.getElementById(form) || document.forms[form]);
2557             }
2558
2559             var el, name, val, disabled, data = '', hasSubmit = false;
2560             for (var i = 0; i < form.elements.length; i++) {
2561                 el = form.elements[i];
2562                 disabled = form.elements[i].disabled;
2563                 name = form.elements[i].name;
2564                 val = form.elements[i].value;
2565
2566                 if (!disabled && name){
2567                     switch (el.type)
2568                             {
2569                         case 'select-one':
2570                         case 'select-multiple':
2571                             for (var j = 0; j < el.options.length; j++) {
2572                                 if (el.options[j].selected) {
2573                                     if (Roo.isIE) {
2574                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2575                                     }
2576                                     else {
2577                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2578                                     }
2579                                 }
2580                             }
2581                             break;
2582                         case 'radio':
2583                         case 'checkbox':
2584                             if (el.checked) {
2585                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2586                             }
2587                             break;
2588                         case 'file':
2589
2590                         case undefined:
2591
2592                         case 'reset':
2593
2594                         case 'button':
2595
2596                             break;
2597                         case 'submit':
2598                             if(hasSubmit == false) {
2599                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2600                                 hasSubmit = true;
2601                             }
2602                             break;
2603                         default:
2604                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2605                             break;
2606                     }
2607                 }
2608             }
2609             data = data.substr(0, data.length - 1);
2610             return data;
2611         },
2612
2613         headers:{},
2614
2615         hasHeaders:false,
2616
2617         useDefaultHeader:true,
2618
2619         defaultPostHeader:'application/x-www-form-urlencoded',
2620
2621         useDefaultXhrHeader:true,
2622
2623         defaultXhrHeader:'XMLHttpRequest',
2624
2625         hasDefaultHeaders:true,
2626
2627         defaultHeaders:{},
2628
2629         poll:{},
2630
2631         timeout:{},
2632
2633         pollInterval:50,
2634
2635         transactionId:0,
2636
2637         setProgId:function(id)
2638         {
2639             this.activeX.unshift(id);
2640         },
2641
2642         setDefaultPostHeader:function(b)
2643         {
2644             this.useDefaultHeader = b;
2645         },
2646
2647         setDefaultXhrHeader:function(b)
2648         {
2649             this.useDefaultXhrHeader = b;
2650         },
2651
2652         setPollingInterval:function(i)
2653         {
2654             if (typeof i == 'number' && isFinite(i)) {
2655                 this.pollInterval = i;
2656             }
2657         },
2658
2659         createXhrObject:function(transactionId)
2660         {
2661             var obj,http;
2662             try
2663             {
2664
2665                 http = new XMLHttpRequest();
2666
2667                 obj = { conn:http, tId:transactionId };
2668             }
2669             catch(e)
2670             {
2671                 for (var i = 0; i < this.activeX.length; ++i) {
2672                     try
2673                     {
2674
2675                         http = new ActiveXObject(this.activeX[i]);
2676
2677                         obj = { conn:http, tId:transactionId };
2678                         break;
2679                     }
2680                     catch(e) {
2681                     }
2682                 }
2683             }
2684             finally
2685             {
2686                 return obj;
2687             }
2688         },
2689
2690         getConnectionObject:function()
2691         {
2692             var o;
2693             var tId = this.transactionId;
2694
2695             try
2696             {
2697                 o = this.createXhrObject(tId);
2698                 if (o) {
2699                     this.transactionId++;
2700                 }
2701             }
2702             catch(e) {
2703             }
2704             finally
2705             {
2706                 return o;
2707             }
2708         },
2709
2710         asyncRequest:function(method, uri, callback, postData)
2711         {
2712             var o = this.getConnectionObject();
2713
2714             if (!o) {
2715                 return null;
2716             }
2717             else {
2718                 o.conn.open(method, uri, true);
2719
2720                 if (this.useDefaultXhrHeader) {
2721                     if (!this.defaultHeaders['X-Requested-With']) {
2722                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2723                     }
2724                 }
2725
2726                 if(postData && this.useDefaultHeader){
2727                     this.initHeader('Content-Type', this.defaultPostHeader);
2728                 }
2729
2730                  if (this.hasDefaultHeaders || this.hasHeaders) {
2731                     this.setHeader(o);
2732                 }
2733
2734                 this.handleReadyState(o, callback);
2735                 o.conn.send(postData || null);
2736
2737                 return o;
2738             }
2739         },
2740
2741         handleReadyState:function(o, callback)
2742         {
2743             var oConn = this;
2744
2745             if (callback && callback.timeout) {
2746                 
2747                 this.timeout[o.tId] = window.setTimeout(function() {
2748                     oConn.abort(o, callback, true);
2749                 }, callback.timeout);
2750             }
2751
2752             this.poll[o.tId] = window.setInterval(
2753                     function() {
2754                         if (o.conn && o.conn.readyState == 4) {
2755                             window.clearInterval(oConn.poll[o.tId]);
2756                             delete oConn.poll[o.tId];
2757
2758                             if(callback && callback.timeout) {
2759                                 window.clearTimeout(oConn.timeout[o.tId]);
2760                                 delete oConn.timeout[o.tId];
2761                             }
2762
2763                             oConn.handleTransactionResponse(o, callback);
2764                         }
2765                     }
2766                     , this.pollInterval);
2767         },
2768
2769         handleTransactionResponse:function(o, callback, isAbort)
2770         {
2771
2772             if (!callback) {
2773                 this.releaseObject(o);
2774                 return;
2775             }
2776
2777             var httpStatus, responseObject;
2778
2779             try
2780             {
2781                 if (o.conn.status !== undefined && o.conn.status != 0) {
2782                     httpStatus = o.conn.status;
2783                 }
2784                 else {
2785                     httpStatus = 13030;
2786                 }
2787             }
2788             catch(e) {
2789
2790
2791                 httpStatus = 13030;
2792             }
2793
2794             if (httpStatus >= 200 && httpStatus < 300) {
2795                 responseObject = this.createResponseObject(o, callback.argument);
2796                 if (callback.success) {
2797                     if (!callback.scope) {
2798                         callback.success(responseObject);
2799                     }
2800                     else {
2801
2802
2803                         callback.success.apply(callback.scope, [responseObject]);
2804                     }
2805                 }
2806             }
2807             else {
2808                 switch (httpStatus) {
2809
2810                     case 12002:
2811                     case 12029:
2812                     case 12030:
2813                     case 12031:
2814                     case 12152:
2815                     case 13030:
2816                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2817                         if (callback.failure) {
2818                             if (!callback.scope) {
2819                                 callback.failure(responseObject);
2820                             }
2821                             else {
2822                                 callback.failure.apply(callback.scope, [responseObject]);
2823                             }
2824                         }
2825                         break;
2826                     default:
2827                         responseObject = this.createResponseObject(o, callback.argument);
2828                         if (callback.failure) {
2829                             if (!callback.scope) {
2830                                 callback.failure(responseObject);
2831                             }
2832                             else {
2833                                 callback.failure.apply(callback.scope, [responseObject]);
2834                             }
2835                         }
2836                 }
2837             }
2838
2839             this.releaseObject(o);
2840             responseObject = null;
2841         },
2842
2843         createResponseObject:function(o, callbackArg)
2844         {
2845             var obj = {};
2846             var headerObj = {};
2847
2848             try
2849             {
2850                 var headerStr = o.conn.getAllResponseHeaders();
2851                 var header = headerStr.split('\n');
2852                 for (var i = 0; i < header.length; i++) {
2853                     var delimitPos = header[i].indexOf(':');
2854                     if (delimitPos != -1) {
2855                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2856                     }
2857                 }
2858             }
2859             catch(e) {
2860             }
2861
2862             obj.tId = o.tId;
2863             obj.status = o.conn.status;
2864             obj.statusText = o.conn.statusText;
2865             obj.getResponseHeader = headerObj;
2866             obj.getAllResponseHeaders = headerStr;
2867             obj.responseText = o.conn.responseText;
2868             obj.responseXML = o.conn.responseXML;
2869
2870             if (typeof callbackArg !== undefined) {
2871                 obj.argument = callbackArg;
2872             }
2873
2874             return obj;
2875         },
2876
2877         createExceptionObject:function(tId, callbackArg, isAbort)
2878         {
2879             var COMM_CODE = 0;
2880             var COMM_ERROR = 'communication failure';
2881             var ABORT_CODE = -1;
2882             var ABORT_ERROR = 'transaction aborted';
2883
2884             var obj = {};
2885
2886             obj.tId = tId;
2887             if (isAbort) {
2888                 obj.status = ABORT_CODE;
2889                 obj.statusText = ABORT_ERROR;
2890             }
2891             else {
2892                 obj.status = COMM_CODE;
2893                 obj.statusText = COMM_ERROR;
2894             }
2895
2896             if (callbackArg) {
2897                 obj.argument = callbackArg;
2898             }
2899
2900             return obj;
2901         },
2902
2903         initHeader:function(label, value, isDefault)
2904         {
2905             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2906
2907             if (headerObj[label] === undefined) {
2908                 headerObj[label] = value;
2909             }
2910             else {
2911
2912
2913                 headerObj[label] = value + "," + headerObj[label];
2914             }
2915
2916             if (isDefault) {
2917                 this.hasDefaultHeaders = true;
2918             }
2919             else {
2920                 this.hasHeaders = true;
2921             }
2922         },
2923
2924
2925         setHeader:function(o)
2926         {
2927             if (this.hasDefaultHeaders) {
2928                 for (var prop in this.defaultHeaders) {
2929                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2930                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2931                     }
2932                 }
2933             }
2934
2935             if (this.hasHeaders) {
2936                 for (var prop in this.headers) {
2937                     if (this.headers.hasOwnProperty(prop)) {
2938                         o.conn.setRequestHeader(prop, this.headers[prop]);
2939                     }
2940                 }
2941                 this.headers = {};
2942                 this.hasHeaders = false;
2943             }
2944         },
2945
2946         resetDefaultHeaders:function() {
2947             delete this.defaultHeaders;
2948             this.defaultHeaders = {};
2949             this.hasDefaultHeaders = false;
2950         },
2951
2952         abort:function(o, callback, isTimeout)
2953         {
2954             if(this.isCallInProgress(o)) {
2955                 o.conn.abort();
2956                 window.clearInterval(this.poll[o.tId]);
2957                 delete this.poll[o.tId];
2958                 if (isTimeout) {
2959                     delete this.timeout[o.tId];
2960                 }
2961
2962                 this.handleTransactionResponse(o, callback, true);
2963
2964                 return true;
2965             }
2966             else {
2967                 return false;
2968             }
2969         },
2970
2971
2972         isCallInProgress:function(o)
2973         {
2974             if (o && o.conn) {
2975                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2976             }
2977             else {
2978
2979                 return false;
2980             }
2981         },
2982
2983
2984         releaseObject:function(o)
2985         {
2986
2987             o.conn = null;
2988
2989             o = null;
2990         },
2991
2992         activeX:[
2993         'MSXML2.XMLHTTP.3.0',
2994         'MSXML2.XMLHTTP',
2995         'Microsoft.XMLHTTP'
2996         ]
2997
2998
2999     };
3000 })();/*
3001  * Portions of this file are based on pieces of Yahoo User Interface Library
3002  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3003  * YUI licensed under the BSD License:
3004  * http://developer.yahoo.net/yui/license.txt
3005  * <script type="text/javascript">
3006  *
3007  */
3008
3009 Roo.lib.Region = function(t, r, b, l) {
3010     this.top = t;
3011     this[1] = t;
3012     this.right = r;
3013     this.bottom = b;
3014     this.left = l;
3015     this[0] = l;
3016 };
3017
3018
3019 Roo.lib.Region.prototype = {
3020     contains : function(region) {
3021         return ( region.left >= this.left &&
3022                  region.right <= this.right &&
3023                  region.top >= this.top &&
3024                  region.bottom <= this.bottom    );
3025
3026     },
3027
3028     getArea : function() {
3029         return ( (this.bottom - this.top) * (this.right - this.left) );
3030     },
3031
3032     intersect : function(region) {
3033         var t = Math.max(this.top, region.top);
3034         var r = Math.min(this.right, region.right);
3035         var b = Math.min(this.bottom, region.bottom);
3036         var l = Math.max(this.left, region.left);
3037
3038         if (b >= t && r >= l) {
3039             return new Roo.lib.Region(t, r, b, l);
3040         } else {
3041             return null;
3042         }
3043     },
3044     union : function(region) {
3045         var t = Math.min(this.top, region.top);
3046         var r = Math.max(this.right, region.right);
3047         var b = Math.max(this.bottom, region.bottom);
3048         var l = Math.min(this.left, region.left);
3049
3050         return new Roo.lib.Region(t, r, b, l);
3051     },
3052
3053     adjust : function(t, l, b, r) {
3054         this.top += t;
3055         this.left += l;
3056         this.right += r;
3057         this.bottom += b;
3058         return this;
3059     }
3060 };
3061
3062 Roo.lib.Region.getRegion = function(el) {
3063     var p = Roo.lib.Dom.getXY(el);
3064
3065     var t = p[1];
3066     var r = p[0] + el.offsetWidth;
3067     var b = p[1] + el.offsetHeight;
3068     var l = p[0];
3069
3070     return new Roo.lib.Region(t, r, b, l);
3071 };
3072 /*
3073  * Portions of this file are based on pieces of Yahoo User Interface Library
3074  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3075  * YUI licensed under the BSD License:
3076  * http://developer.yahoo.net/yui/license.txt
3077  * <script type="text/javascript">
3078  *
3079  */
3080 //@@dep Roo.lib.Region
3081
3082
3083 Roo.lib.Point = function(x, y) {
3084     if (x instanceof Array) {
3085         y = x[1];
3086         x = x[0];
3087     }
3088     this.x = this.right = this.left = this[0] = x;
3089     this.y = this.top = this.bottom = this[1] = y;
3090 };
3091
3092 Roo.lib.Point.prototype = new Roo.lib.Region();
3093 /*
3094  * Portions of this file are based on pieces of Yahoo User Interface Library
3095  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3096  * YUI licensed under the BSD License:
3097  * http://developer.yahoo.net/yui/license.txt
3098  * <script type="text/javascript">
3099  *
3100  */
3101  
3102 (function() {   
3103
3104     Roo.lib.Anim = {
3105         scroll : function(el, args, duration, easing, cb, scope) {
3106             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3107         },
3108
3109         motion : function(el, args, duration, easing, cb, scope) {
3110             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3111         },
3112
3113         color : function(el, args, duration, easing, cb, scope) {
3114             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3115         },
3116
3117         run : function(el, args, duration, easing, cb, scope, type) {
3118             type = type || Roo.lib.AnimBase;
3119             if (typeof easing == "string") {
3120                 easing = Roo.lib.Easing[easing];
3121             }
3122             var anim = new type(el, args, duration, easing);
3123             anim.animateX(function() {
3124                 Roo.callback(cb, scope);
3125             });
3126             return anim;
3127         }
3128     };
3129 })();/*
3130  * Portions of this file are based on pieces of Yahoo User Interface Library
3131  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3132  * YUI licensed under the BSD License:
3133  * http://developer.yahoo.net/yui/license.txt
3134  * <script type="text/javascript">
3135  *
3136  */
3137
3138 (function() {    
3139     var libFlyweight;
3140     
3141     function fly(el) {
3142         if (!libFlyweight) {
3143             libFlyweight = new Roo.Element.Flyweight();
3144         }
3145         libFlyweight.dom = el;
3146         return libFlyweight;
3147     }
3148
3149     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3150     
3151    
3152     
3153     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3154         if (el) {
3155             this.init(el, attributes, duration, method);
3156         }
3157     };
3158
3159     Roo.lib.AnimBase.fly = fly;
3160     
3161     
3162     
3163     Roo.lib.AnimBase.prototype = {
3164
3165         toString: function() {
3166             var el = this.getEl();
3167             var id = el.id || el.tagName;
3168             return ("Anim " + id);
3169         },
3170
3171         patterns: {
3172             noNegatives:        /width|height|opacity|padding/i,
3173             offsetAttribute:  /^((width|height)|(top|left))$/,
3174             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3175             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3176         },
3177
3178
3179         doMethod: function(attr, start, end) {
3180             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3181         },
3182
3183
3184         setAttribute: function(attr, val, unit) {
3185             if (this.patterns.noNegatives.test(attr)) {
3186                 val = (val > 0) ? val : 0;
3187             }
3188
3189             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3190         },
3191
3192
3193         getAttribute: function(attr) {
3194             var el = this.getEl();
3195             var val = fly(el).getStyle(attr);
3196
3197             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3198                 return parseFloat(val);
3199             }
3200
3201             var a = this.patterns.offsetAttribute.exec(attr) || [];
3202             var pos = !!( a[3] );
3203             var box = !!( a[2] );
3204
3205
3206             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3207                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3208             } else {
3209                 val = 0;
3210             }
3211
3212             return val;
3213         },
3214
3215
3216         getDefaultUnit: function(attr) {
3217             if (this.patterns.defaultUnit.test(attr)) {
3218                 return 'px';
3219             }
3220
3221             return '';
3222         },
3223
3224         animateX : function(callback, scope) {
3225             var f = function() {
3226                 this.onComplete.removeListener(f);
3227                 if (typeof callback == "function") {
3228                     callback.call(scope || this, this);
3229                 }
3230             };
3231             this.onComplete.addListener(f, this);
3232             this.animate();
3233         },
3234
3235
3236         setRuntimeAttribute: function(attr) {
3237             var start;
3238             var end;
3239             var attributes = this.attributes;
3240
3241             this.runtimeAttributes[attr] = {};
3242
3243             var isset = function(prop) {
3244                 return (typeof prop !== 'undefined');
3245             };
3246
3247             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3248                 return false;
3249             }
3250
3251             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3252
3253
3254             if (isset(attributes[attr]['to'])) {
3255                 end = attributes[attr]['to'];
3256             } else if (isset(attributes[attr]['by'])) {
3257                 if (start.constructor == Array) {
3258                     end = [];
3259                     for (var i = 0, len = start.length; i < len; ++i) {
3260                         end[i] = start[i] + attributes[attr]['by'][i];
3261                     }
3262                 } else {
3263                     end = start + attributes[attr]['by'];
3264                 }
3265             }
3266
3267             this.runtimeAttributes[attr].start = start;
3268             this.runtimeAttributes[attr].end = end;
3269
3270
3271             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3272         },
3273
3274
3275         init: function(el, attributes, duration, method) {
3276
3277             var isAnimated = false;
3278
3279
3280             var startTime = null;
3281
3282
3283             var actualFrames = 0;
3284
3285
3286             el = Roo.getDom(el);
3287
3288
3289             this.attributes = attributes || {};
3290
3291
3292             this.duration = duration || 1;
3293
3294
3295             this.method = method || Roo.lib.Easing.easeNone;
3296
3297
3298             this.useSeconds = true;
3299
3300
3301             this.currentFrame = 0;
3302
3303
3304             this.totalFrames = Roo.lib.AnimMgr.fps;
3305
3306
3307             this.getEl = function() {
3308                 return el;
3309             };
3310
3311
3312             this.isAnimated = function() {
3313                 return isAnimated;
3314             };
3315
3316
3317             this.getStartTime = function() {
3318                 return startTime;
3319             };
3320
3321             this.runtimeAttributes = {};
3322
3323
3324             this.animate = function() {
3325                 if (this.isAnimated()) {
3326                     return false;
3327                 }
3328
3329                 this.currentFrame = 0;
3330
3331                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3332
3333                 Roo.lib.AnimMgr.registerElement(this);
3334             };
3335
3336
3337             this.stop = function(finish) {
3338                 if (finish) {
3339                     this.currentFrame = this.totalFrames;
3340                     this._onTween.fire();
3341                 }
3342                 Roo.lib.AnimMgr.stop(this);
3343             };
3344
3345             var onStart = function() {
3346                 this.onStart.fire();
3347
3348                 this.runtimeAttributes = {};
3349                 for (var attr in this.attributes) {
3350                     this.setRuntimeAttribute(attr);
3351                 }
3352
3353                 isAnimated = true;
3354                 actualFrames = 0;
3355                 startTime = new Date();
3356             };
3357
3358
3359             var onTween = function() {
3360                 var data = {
3361                     duration: new Date() - this.getStartTime(),
3362                     currentFrame: this.currentFrame
3363                 };
3364
3365                 data.toString = function() {
3366                     return (
3367                             'duration: ' + data.duration +
3368                             ', currentFrame: ' + data.currentFrame
3369                             );
3370                 };
3371
3372                 this.onTween.fire(data);
3373
3374                 var runtimeAttributes = this.runtimeAttributes;
3375
3376                 for (var attr in runtimeAttributes) {
3377                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3378                 }
3379
3380                 actualFrames += 1;
3381             };
3382
3383             var onComplete = function() {
3384                 var actual_duration = (new Date() - startTime) / 1000 ;
3385
3386                 var data = {
3387                     duration: actual_duration,
3388                     frames: actualFrames,
3389                     fps: actualFrames / actual_duration
3390                 };
3391
3392                 data.toString = function() {
3393                     return (
3394                             'duration: ' + data.duration +
3395                             ', frames: ' + data.frames +
3396                             ', fps: ' + data.fps
3397                             );
3398                 };
3399
3400                 isAnimated = false;
3401                 actualFrames = 0;
3402                 this.onComplete.fire(data);
3403             };
3404
3405
3406             this._onStart = new Roo.util.Event(this);
3407             this.onStart = new Roo.util.Event(this);
3408             this.onTween = new Roo.util.Event(this);
3409             this._onTween = new Roo.util.Event(this);
3410             this.onComplete = new Roo.util.Event(this);
3411             this._onComplete = new Roo.util.Event(this);
3412             this._onStart.addListener(onStart);
3413             this._onTween.addListener(onTween);
3414             this._onComplete.addListener(onComplete);
3415         }
3416     };
3417 })();
3418 /*
3419  * Portions of this file are based on pieces of Yahoo User Interface Library
3420  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3421  * YUI licensed under the BSD License:
3422  * http://developer.yahoo.net/yui/license.txt
3423  * <script type="text/javascript">
3424  *
3425  */
3426
3427 Roo.lib.AnimMgr = new function() {
3428
3429     var thread = null;
3430
3431
3432     var queue = [];
3433
3434
3435     var tweenCount = 0;
3436
3437
3438     this.fps = 1000;
3439
3440
3441     this.delay = 1;
3442
3443
3444     this.registerElement = function(tween) {
3445         queue[queue.length] = tween;
3446         tweenCount += 1;
3447         tween._onStart.fire();
3448         this.start();
3449     };
3450
3451
3452     this.unRegister = function(tween, index) {
3453         tween._onComplete.fire();
3454         index = index || getIndex(tween);
3455         if (index != -1) {
3456             queue.splice(index, 1);
3457         }
3458
3459         tweenCount -= 1;
3460         if (tweenCount <= 0) {
3461             this.stop();
3462         }
3463     };
3464
3465
3466     this.start = function() {
3467         if (thread === null) {
3468             thread = setInterval(this.run, this.delay);
3469         }
3470     };
3471
3472
3473     this.stop = function(tween) {
3474         if (!tween) {
3475             clearInterval(thread);
3476
3477             for (var i = 0, len = queue.length; i < len; ++i) {
3478                 if (queue[0].isAnimated()) {
3479                     this.unRegister(queue[0], 0);
3480                 }
3481             }
3482
3483             queue = [];
3484             thread = null;
3485             tweenCount = 0;
3486         }
3487         else {
3488             this.unRegister(tween);
3489         }
3490     };
3491
3492
3493     this.run = function() {
3494         for (var i = 0, len = queue.length; i < len; ++i) {
3495             var tween = queue[i];
3496             if (!tween || !tween.isAnimated()) {
3497                 continue;
3498             }
3499
3500             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3501             {
3502                 tween.currentFrame += 1;
3503
3504                 if (tween.useSeconds) {
3505                     correctFrame(tween);
3506                 }
3507                 tween._onTween.fire();
3508             }
3509             else {
3510                 Roo.lib.AnimMgr.stop(tween, i);
3511             }
3512         }
3513     };
3514
3515     var getIndex = function(anim) {
3516         for (var i = 0, len = queue.length; i < len; ++i) {
3517             if (queue[i] == anim) {
3518                 return i;
3519             }
3520         }
3521         return -1;
3522     };
3523
3524
3525     var correctFrame = function(tween) {
3526         var frames = tween.totalFrames;
3527         var frame = tween.currentFrame;
3528         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3529         var elapsed = (new Date() - tween.getStartTime());
3530         var tweak = 0;
3531
3532         if (elapsed < tween.duration * 1000) {
3533             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3534         } else {
3535             tweak = frames - (frame + 1);
3536         }
3537         if (tweak > 0 && isFinite(tweak)) {
3538             if (tween.currentFrame + tweak >= frames) {
3539                 tweak = frames - (frame + 1);
3540             }
3541
3542             tween.currentFrame += tweak;
3543         }
3544     };
3545 };
3546
3547     /*
3548  * Portions of this file are based on pieces of Yahoo User Interface Library
3549  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3550  * YUI licensed under the BSD License:
3551  * http://developer.yahoo.net/yui/license.txt
3552  * <script type="text/javascript">
3553  *
3554  */
3555 Roo.lib.Bezier = new function() {
3556
3557         this.getPosition = function(points, t) {
3558             var n = points.length;
3559             var tmp = [];
3560
3561             for (var i = 0; i < n; ++i) {
3562                 tmp[i] = [points[i][0], points[i][1]];
3563             }
3564
3565             for (var j = 1; j < n; ++j) {
3566                 for (i = 0; i < n - j; ++i) {
3567                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3568                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3569                 }
3570             }
3571
3572             return [ tmp[0][0], tmp[0][1] ];
3573
3574         };
3575     };/*
3576  * Portions of this file are based on pieces of Yahoo User Interface Library
3577  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3578  * YUI licensed under the BSD License:
3579  * http://developer.yahoo.net/yui/license.txt
3580  * <script type="text/javascript">
3581  *
3582  */
3583 (function() {
3584
3585     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3586         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3587     };
3588
3589     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3590
3591     var fly = Roo.lib.AnimBase.fly;
3592     var Y = Roo.lib;
3593     var superclass = Y.ColorAnim.superclass;
3594     var proto = Y.ColorAnim.prototype;
3595
3596     proto.toString = function() {
3597         var el = this.getEl();
3598         var id = el.id || el.tagName;
3599         return ("ColorAnim " + id);
3600     };
3601
3602     proto.patterns.color = /color$/i;
3603     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3604     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3605     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3606     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3607
3608
3609     proto.parseColor = function(s) {
3610         if (s.length == 3) {
3611             return s;
3612         }
3613
3614         var c = this.patterns.hex.exec(s);
3615         if (c && c.length == 4) {
3616             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3617         }
3618
3619         c = this.patterns.rgb.exec(s);
3620         if (c && c.length == 4) {
3621             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3622         }
3623
3624         c = this.patterns.hex3.exec(s);
3625         if (c && c.length == 4) {
3626             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3627         }
3628
3629         return null;
3630     };
3631     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3632     proto.getAttribute = function(attr) {
3633         var el = this.getEl();
3634         if (this.patterns.color.test(attr)) {
3635             var val = fly(el).getStyle(attr);
3636
3637             if (this.patterns.transparent.test(val)) {
3638                 var parent = el.parentNode;
3639                 val = fly(parent).getStyle(attr);
3640
3641                 while (parent && this.patterns.transparent.test(val)) {
3642                     parent = parent.parentNode;
3643                     val = fly(parent).getStyle(attr);
3644                     if (parent.tagName.toUpperCase() == 'HTML') {
3645                         val = '#fff';
3646                     }
3647                 }
3648             }
3649         } else {
3650             val = superclass.getAttribute.call(this, attr);
3651         }
3652
3653         return val;
3654     };
3655     proto.getAttribute = function(attr) {
3656         var el = this.getEl();
3657         if (this.patterns.color.test(attr)) {
3658             var val = fly(el).getStyle(attr);
3659
3660             if (this.patterns.transparent.test(val)) {
3661                 var parent = el.parentNode;
3662                 val = fly(parent).getStyle(attr);
3663
3664                 while (parent && this.patterns.transparent.test(val)) {
3665                     parent = parent.parentNode;
3666                     val = fly(parent).getStyle(attr);
3667                     if (parent.tagName.toUpperCase() == 'HTML') {
3668                         val = '#fff';
3669                     }
3670                 }
3671             }
3672         } else {
3673             val = superclass.getAttribute.call(this, attr);
3674         }
3675
3676         return val;
3677     };
3678
3679     proto.doMethod = function(attr, start, end) {
3680         var val;
3681
3682         if (this.patterns.color.test(attr)) {
3683             val = [];
3684             for (var i = 0, len = start.length; i < len; ++i) {
3685                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3686             }
3687
3688             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3689         }
3690         else {
3691             val = superclass.doMethod.call(this, attr, start, end);
3692         }
3693
3694         return val;
3695     };
3696
3697     proto.setRuntimeAttribute = function(attr) {
3698         superclass.setRuntimeAttribute.call(this, attr);
3699
3700         if (this.patterns.color.test(attr)) {
3701             var attributes = this.attributes;
3702             var start = this.parseColor(this.runtimeAttributes[attr].start);
3703             var end = this.parseColor(this.runtimeAttributes[attr].end);
3704
3705             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3706                 end = this.parseColor(attributes[attr].by);
3707
3708                 for (var i = 0, len = start.length; i < len; ++i) {
3709                     end[i] = start[i] + end[i];
3710                 }
3711             }
3712
3713             this.runtimeAttributes[attr].start = start;
3714             this.runtimeAttributes[attr].end = end;
3715         }
3716     };
3717 })();
3718
3719 /*
3720  * Portions of this file are based on pieces of Yahoo User Interface Library
3721  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3722  * YUI licensed under the BSD License:
3723  * http://developer.yahoo.net/yui/license.txt
3724  * <script type="text/javascript">
3725  *
3726  */
3727 Roo.lib.Easing = {
3728
3729
3730     easeNone: function (t, b, c, d) {
3731         return c * t / d + b;
3732     },
3733
3734
3735     easeIn: function (t, b, c, d) {
3736         return c * (t /= d) * t + b;
3737     },
3738
3739
3740     easeOut: function (t, b, c, d) {
3741         return -c * (t /= d) * (t - 2) + b;
3742     },
3743
3744
3745     easeBoth: function (t, b, c, d) {
3746         if ((t /= d / 2) < 1) {
3747             return c / 2 * t * t + b;
3748         }
3749
3750         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3751     },
3752
3753
3754     easeInStrong: function (t, b, c, d) {
3755         return c * (t /= d) * t * t * t + b;
3756     },
3757
3758
3759     easeOutStrong: function (t, b, c, d) {
3760         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3761     },
3762
3763
3764     easeBothStrong: function (t, b, c, d) {
3765         if ((t /= d / 2) < 1) {
3766             return c / 2 * t * t * t * t + b;
3767         }
3768
3769         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3770     },
3771
3772
3773
3774     elasticIn: function (t, b, c, d, a, p) {
3775         if (t == 0) {
3776             return b;
3777         }
3778         if ((t /= d) == 1) {
3779             return b + c;
3780         }
3781         if (!p) {
3782             p = d * .3;
3783         }
3784
3785         if (!a || a < Math.abs(c)) {
3786             a = c;
3787             var s = p / 4;
3788         }
3789         else {
3790             var s = p / (2 * Math.PI) * Math.asin(c / a);
3791         }
3792
3793         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3794     },
3795
3796
3797     elasticOut: function (t, b, c, d, a, p) {
3798         if (t == 0) {
3799             return b;
3800         }
3801         if ((t /= d) == 1) {
3802             return b + c;
3803         }
3804         if (!p) {
3805             p = d * .3;
3806         }
3807
3808         if (!a || a < Math.abs(c)) {
3809             a = c;
3810             var s = p / 4;
3811         }
3812         else {
3813             var s = p / (2 * Math.PI) * Math.asin(c / a);
3814         }
3815
3816         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3817     },
3818
3819
3820     elasticBoth: function (t, b, c, d, a, p) {
3821         if (t == 0) {
3822             return b;
3823         }
3824
3825         if ((t /= d / 2) == 2) {
3826             return b + c;
3827         }
3828
3829         if (!p) {
3830             p = d * (.3 * 1.5);
3831         }
3832
3833         if (!a || a < Math.abs(c)) {
3834             a = c;
3835             var s = p / 4;
3836         }
3837         else {
3838             var s = p / (2 * Math.PI) * Math.asin(c / a);
3839         }
3840
3841         if (t < 1) {
3842             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3843                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3844         }
3845         return a * Math.pow(2, -10 * (t -= 1)) *
3846                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3847     },
3848
3849
3850
3851     backIn: function (t, b, c, d, s) {
3852         if (typeof s == 'undefined') {
3853             s = 1.70158;
3854         }
3855         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3856     },
3857
3858
3859     backOut: function (t, b, c, d, s) {
3860         if (typeof s == 'undefined') {
3861             s = 1.70158;
3862         }
3863         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3864     },
3865
3866
3867     backBoth: function (t, b, c, d, s) {
3868         if (typeof s == 'undefined') {
3869             s = 1.70158;
3870         }
3871
3872         if ((t /= d / 2 ) < 1) {
3873             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3874         }
3875         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3876     },
3877
3878
3879     bounceIn: function (t, b, c, d) {
3880         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3881     },
3882
3883
3884     bounceOut: function (t, b, c, d) {
3885         if ((t /= d) < (1 / 2.75)) {
3886             return c * (7.5625 * t * t) + b;
3887         } else if (t < (2 / 2.75)) {
3888             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3889         } else if (t < (2.5 / 2.75)) {
3890             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3891         }
3892         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3893     },
3894
3895
3896     bounceBoth: function (t, b, c, d) {
3897         if (t < d / 2) {
3898             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3899         }
3900         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3901     }
3902 };/*
3903  * Portions of this file are based on pieces of Yahoo User Interface Library
3904  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3905  * YUI licensed under the BSD License:
3906  * http://developer.yahoo.net/yui/license.txt
3907  * <script type="text/javascript">
3908  *
3909  */
3910     (function() {
3911         Roo.lib.Motion = function(el, attributes, duration, method) {
3912             if (el) {
3913                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3914             }
3915         };
3916
3917         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3918
3919
3920         var Y = Roo.lib;
3921         var superclass = Y.Motion.superclass;
3922         var proto = Y.Motion.prototype;
3923
3924         proto.toString = function() {
3925             var el = this.getEl();
3926             var id = el.id || el.tagName;
3927             return ("Motion " + id);
3928         };
3929
3930         proto.patterns.points = /^points$/i;
3931
3932         proto.setAttribute = function(attr, val, unit) {
3933             if (this.patterns.points.test(attr)) {
3934                 unit = unit || 'px';
3935                 superclass.setAttribute.call(this, 'left', val[0], unit);
3936                 superclass.setAttribute.call(this, 'top', val[1], unit);
3937             } else {
3938                 superclass.setAttribute.call(this, attr, val, unit);
3939             }
3940         };
3941
3942         proto.getAttribute = function(attr) {
3943             if (this.patterns.points.test(attr)) {
3944                 var val = [
3945                         superclass.getAttribute.call(this, 'left'),
3946                         superclass.getAttribute.call(this, 'top')
3947                         ];
3948             } else {
3949                 val = superclass.getAttribute.call(this, attr);
3950             }
3951
3952             return val;
3953         };
3954
3955         proto.doMethod = function(attr, start, end) {
3956             var val = null;
3957
3958             if (this.patterns.points.test(attr)) {
3959                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3960                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3961             } else {
3962                 val = superclass.doMethod.call(this, attr, start, end);
3963             }
3964             return val;
3965         };
3966
3967         proto.setRuntimeAttribute = function(attr) {
3968             if (this.patterns.points.test(attr)) {
3969                 var el = this.getEl();
3970                 var attributes = this.attributes;
3971                 var start;
3972                 var control = attributes['points']['control'] || [];
3973                 var end;
3974                 var i, len;
3975
3976                 if (control.length > 0 && !(control[0] instanceof Array)) {
3977                     control = [control];
3978                 } else {
3979                     var tmp = [];
3980                     for (i = 0,len = control.length; i < len; ++i) {
3981                         tmp[i] = control[i];
3982                     }
3983                     control = tmp;
3984                 }
3985
3986                 Roo.fly(el).position();
3987
3988                 if (isset(attributes['points']['from'])) {
3989                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3990                 }
3991                 else {
3992                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3993                 }
3994
3995                 start = this.getAttribute('points');
3996
3997
3998                 if (isset(attributes['points']['to'])) {
3999                     end = translateValues.call(this, attributes['points']['to'], start);
4000
4001                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4002                     for (i = 0,len = control.length; i < len; ++i) {
4003                         control[i] = translateValues.call(this, control[i], start);
4004                     }
4005
4006
4007                 } else if (isset(attributes['points']['by'])) {
4008                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4009
4010                     for (i = 0,len = control.length; i < len; ++i) {
4011                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4012                     }
4013                 }
4014
4015                 this.runtimeAttributes[attr] = [start];
4016
4017                 if (control.length > 0) {
4018                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4019                 }
4020
4021                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4022             }
4023             else {
4024                 superclass.setRuntimeAttribute.call(this, attr);
4025             }
4026         };
4027
4028         var translateValues = function(val, start) {
4029             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4030             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4031
4032             return val;
4033         };
4034
4035         var isset = function(prop) {
4036             return (typeof prop !== 'undefined');
4037         };
4038     })();
4039 /*
4040  * Portions of this file are based on pieces of Yahoo User Interface Library
4041  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4042  * YUI licensed under the BSD License:
4043  * http://developer.yahoo.net/yui/license.txt
4044  * <script type="text/javascript">
4045  *
4046  */
4047     (function() {
4048         Roo.lib.Scroll = function(el, attributes, duration, method) {
4049             if (el) {
4050                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4051             }
4052         };
4053
4054         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4055
4056
4057         var Y = Roo.lib;
4058         var superclass = Y.Scroll.superclass;
4059         var proto = Y.Scroll.prototype;
4060
4061         proto.toString = function() {
4062             var el = this.getEl();
4063             var id = el.id || el.tagName;
4064             return ("Scroll " + id);
4065         };
4066
4067         proto.doMethod = function(attr, start, end) {
4068             var val = null;
4069
4070             if (attr == 'scroll') {
4071                 val = [
4072                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4073                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4074                         ];
4075
4076             } else {
4077                 val = superclass.doMethod.call(this, attr, start, end);
4078             }
4079             return val;
4080         };
4081
4082         proto.getAttribute = function(attr) {
4083             var val = null;
4084             var el = this.getEl();
4085
4086             if (attr == 'scroll') {
4087                 val = [ el.scrollLeft, el.scrollTop ];
4088             } else {
4089                 val = superclass.getAttribute.call(this, attr);
4090             }
4091
4092             return val;
4093         };
4094
4095         proto.setAttribute = function(attr, val, unit) {
4096             var el = this.getEl();
4097
4098             if (attr == 'scroll') {
4099                 el.scrollLeft = val[0];
4100                 el.scrollTop = val[1];
4101             } else {
4102                 superclass.setAttribute.call(this, attr, val, unit);
4103             }
4104         };
4105     })();
4106 /*
4107  * Based on:
4108  * Ext JS Library 1.1.1
4109  * Copyright(c) 2006-2007, Ext JS, LLC.
4110  *
4111  * Originally Released Under LGPL - original licence link has changed is not relivant.
4112  *
4113  * Fork - LGPL
4114  * <script type="text/javascript">
4115  */
4116
4117
4118 // nasty IE9 hack - what a pile of crap that is..
4119
4120  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4121     Range.prototype.createContextualFragment = function (html) {
4122         var doc = window.document;
4123         var container = doc.createElement("div");
4124         container.innerHTML = html;
4125         var frag = doc.createDocumentFragment(), n;
4126         while ((n = container.firstChild)) {
4127             frag.appendChild(n);
4128         }
4129         return frag;
4130     };
4131 }
4132
4133 /**
4134  * @class Roo.DomHelper
4135  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4136  * 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>.
4137  * @singleton
4138  */
4139 Roo.DomHelper = function(){
4140     var tempTableEl = null;
4141     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4142     var tableRe = /^table|tbody|tr|td$/i;
4143     var xmlns = {};
4144     // build as innerHTML where available
4145     /** @ignore */
4146     var createHtml = function(o){
4147         if(typeof o == 'string'){
4148             return o;
4149         }
4150         var b = "";
4151         if(!o.tag){
4152             o.tag = "div";
4153         }
4154         b += "<" + o.tag;
4155         for(var attr in o){
4156             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4157             if(attr == "style"){
4158                 var s = o["style"];
4159                 if(typeof s == "function"){
4160                     s = s.call();
4161                 }
4162                 if(typeof s == "string"){
4163                     b += ' style="' + s + '"';
4164                 }else if(typeof s == "object"){
4165                     b += ' style="';
4166                     for(var key in s){
4167                         if(typeof s[key] != "function"){
4168                             b += key + ":" + s[key] + ";";
4169                         }
4170                     }
4171                     b += '"';
4172                 }
4173             }else{
4174                 if(attr == "cls"){
4175                     b += ' class="' + o["cls"] + '"';
4176                 }else if(attr == "htmlFor"){
4177                     b += ' for="' + o["htmlFor"] + '"';
4178                 }else{
4179                     b += " " + attr + '="' + o[attr] + '"';
4180                 }
4181             }
4182         }
4183         if(emptyTags.test(o.tag)){
4184             b += "/>";
4185         }else{
4186             b += ">";
4187             var cn = o.children || o.cn;
4188             if(cn){
4189                 //http://bugs.kde.org/show_bug.cgi?id=71506
4190                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4191                     for(var i = 0, len = cn.length; i < len; i++) {
4192                         b += createHtml(cn[i], b);
4193                     }
4194                 }else{
4195                     b += createHtml(cn, b);
4196                 }
4197             }
4198             if(o.html){
4199                 b += o.html;
4200             }
4201             b += "</" + o.tag + ">";
4202         }
4203         return b;
4204     };
4205
4206     // build as dom
4207     /** @ignore */
4208     var createDom = function(o, parentNode){
4209          
4210         // defininition craeted..
4211         var ns = false;
4212         if (o.ns && o.ns != 'html') {
4213                
4214             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4215                 xmlns[o.ns] = o.xmlns;
4216                 ns = o.xmlns;
4217             }
4218             if (typeof(xmlns[o.ns]) == 'undefined') {
4219                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4220             }
4221             ns = xmlns[o.ns];
4222         }
4223         
4224         
4225         if (typeof(o) == 'string') {
4226             return parentNode.appendChild(document.createTextNode(o));
4227         }
4228         o.tag = o.tag || div;
4229         if (o.ns && Roo.isIE) {
4230             ns = false;
4231             o.tag = o.ns + ':' + o.tag;
4232             
4233         }
4234         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4235         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4236         for(var attr in o){
4237             
4238             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4239                     attr == "style" || typeof o[attr] == "function") { continue; }
4240                     
4241             if(attr=="cls" && Roo.isIE){
4242                 el.className = o["cls"];
4243             }else{
4244                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4245                 else { 
4246                     el[attr] = o[attr];
4247                 }
4248             }
4249         }
4250         Roo.DomHelper.applyStyles(el, o.style);
4251         var cn = o.children || o.cn;
4252         if(cn){
4253             //http://bugs.kde.org/show_bug.cgi?id=71506
4254              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4255                 for(var i = 0, len = cn.length; i < len; i++) {
4256                     createDom(cn[i], el);
4257                 }
4258             }else{
4259                 createDom(cn, el);
4260             }
4261         }
4262         if(o.html){
4263             el.innerHTML = o.html;
4264         }
4265         if(parentNode){
4266            parentNode.appendChild(el);
4267         }
4268         return el;
4269     };
4270
4271     var ieTable = function(depth, s, h, e){
4272         tempTableEl.innerHTML = [s, h, e].join('');
4273         var i = -1, el = tempTableEl;
4274         while(++i < depth){
4275             el = el.firstChild;
4276         }
4277         return el;
4278     };
4279
4280     // kill repeat to save bytes
4281     var ts = '<table>',
4282         te = '</table>',
4283         tbs = ts+'<tbody>',
4284         tbe = '</tbody>'+te,
4285         trs = tbs + '<tr>',
4286         tre = '</tr>'+tbe;
4287
4288     /**
4289      * @ignore
4290      * Nasty code for IE's broken table implementation
4291      */
4292     var insertIntoTable = function(tag, where, el, html){
4293         if(!tempTableEl){
4294             tempTableEl = document.createElement('div');
4295         }
4296         var node;
4297         var before = null;
4298         if(tag == 'td'){
4299             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4300                 return;
4301             }
4302             if(where == 'beforebegin'){
4303                 before = el;
4304                 el = el.parentNode;
4305             } else{
4306                 before = el.nextSibling;
4307                 el = el.parentNode;
4308             }
4309             node = ieTable(4, trs, html, tre);
4310         }
4311         else if(tag == 'tr'){
4312             if(where == 'beforebegin'){
4313                 before = el;
4314                 el = el.parentNode;
4315                 node = ieTable(3, tbs, html, tbe);
4316             } else if(where == 'afterend'){
4317                 before = el.nextSibling;
4318                 el = el.parentNode;
4319                 node = ieTable(3, tbs, html, tbe);
4320             } else{ // INTO a TR
4321                 if(where == 'afterbegin'){
4322                     before = el.firstChild;
4323                 }
4324                 node = ieTable(4, trs, html, tre);
4325             }
4326         } else if(tag == 'tbody'){
4327             if(where == 'beforebegin'){
4328                 before = el;
4329                 el = el.parentNode;
4330                 node = ieTable(2, ts, html, te);
4331             } else if(where == 'afterend'){
4332                 before = el.nextSibling;
4333                 el = el.parentNode;
4334                 node = ieTable(2, ts, html, te);
4335             } else{
4336                 if(where == 'afterbegin'){
4337                     before = el.firstChild;
4338                 }
4339                 node = ieTable(3, tbs, html, tbe);
4340             }
4341         } else{ // TABLE
4342             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4343                 return;
4344             }
4345             if(where == 'afterbegin'){
4346                 before = el.firstChild;
4347             }
4348             node = ieTable(2, ts, html, te);
4349         }
4350         el.insertBefore(node, before);
4351         return node;
4352     };
4353
4354     return {
4355     /** True to force the use of DOM instead of html fragments @type Boolean */
4356     useDom : false,
4357
4358     /**
4359      * Returns the markup for the passed Element(s) config
4360      * @param {Object} o The Dom object spec (and children)
4361      * @return {String}
4362      */
4363     markup : function(o){
4364         return createHtml(o);
4365     },
4366
4367     /**
4368      * Applies a style specification to an element
4369      * @param {String/HTMLElement} el The element to apply styles to
4370      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4371      * a function which returns such a specification.
4372      */
4373     applyStyles : function(el, styles){
4374         if(styles){
4375            el = Roo.fly(el);
4376            if(typeof styles == "string"){
4377                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4378                var matches;
4379                while ((matches = re.exec(styles)) != null){
4380                    el.setStyle(matches[1], matches[2]);
4381                }
4382            }else if (typeof styles == "object"){
4383                for (var style in styles){
4384                   el.setStyle(style, styles[style]);
4385                }
4386            }else if (typeof styles == "function"){
4387                 Roo.DomHelper.applyStyles(el, styles.call());
4388            }
4389         }
4390     },
4391
4392     /**
4393      * Inserts an HTML fragment into the Dom
4394      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4395      * @param {HTMLElement} el The context element
4396      * @param {String} html The HTML fragmenet
4397      * @return {HTMLElement} The new node
4398      */
4399     insertHtml : function(where, el, html){
4400         where = where.toLowerCase();
4401         if(el.insertAdjacentHTML){
4402             if(tableRe.test(el.tagName)){
4403                 var rs;
4404                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4405                     return rs;
4406                 }
4407             }
4408             switch(where){
4409                 case "beforebegin":
4410                     el.insertAdjacentHTML('BeforeBegin', html);
4411                     return el.previousSibling;
4412                 case "afterbegin":
4413                     el.insertAdjacentHTML('AfterBegin', html);
4414                     return el.firstChild;
4415                 case "beforeend":
4416                     el.insertAdjacentHTML('BeforeEnd', html);
4417                     return el.lastChild;
4418                 case "afterend":
4419                     el.insertAdjacentHTML('AfterEnd', html);
4420                     return el.nextSibling;
4421             }
4422             throw 'Illegal insertion point -> "' + where + '"';
4423         }
4424         var range = el.ownerDocument.createRange();
4425         var frag;
4426         switch(where){
4427              case "beforebegin":
4428                 range.setStartBefore(el);
4429                 frag = range.createContextualFragment(html);
4430                 el.parentNode.insertBefore(frag, el);
4431                 return el.previousSibling;
4432              case "afterbegin":
4433                 if(el.firstChild){
4434                     range.setStartBefore(el.firstChild);
4435                     frag = range.createContextualFragment(html);
4436                     el.insertBefore(frag, el.firstChild);
4437                     return el.firstChild;
4438                 }else{
4439                     el.innerHTML = html;
4440                     return el.firstChild;
4441                 }
4442             case "beforeend":
4443                 if(el.lastChild){
4444                     range.setStartAfter(el.lastChild);
4445                     frag = range.createContextualFragment(html);
4446                     el.appendChild(frag);
4447                     return el.lastChild;
4448                 }else{
4449                     el.innerHTML = html;
4450                     return el.lastChild;
4451                 }
4452             case "afterend":
4453                 range.setStartAfter(el);
4454                 frag = range.createContextualFragment(html);
4455                 el.parentNode.insertBefore(frag, el.nextSibling);
4456                 return el.nextSibling;
4457             }
4458             throw 'Illegal insertion point -> "' + where + '"';
4459     },
4460
4461     /**
4462      * Creates new Dom element(s) and inserts them before el
4463      * @param {String/HTMLElement/Element} el The context element
4464      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4465      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4466      * @return {HTMLElement/Roo.Element} The new node
4467      */
4468     insertBefore : function(el, o, returnElement){
4469         return this.doInsert(el, o, returnElement, "beforeBegin");
4470     },
4471
4472     /**
4473      * Creates new Dom element(s) and inserts them after el
4474      * @param {String/HTMLElement/Element} el The context element
4475      * @param {Object} o The Dom object spec (and children)
4476      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4477      * @return {HTMLElement/Roo.Element} The new node
4478      */
4479     insertAfter : function(el, o, returnElement){
4480         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4481     },
4482
4483     /**
4484      * Creates new Dom element(s) and inserts them as the first child of el
4485      * @param {String/HTMLElement/Element} el The context element
4486      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4487      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4488      * @return {HTMLElement/Roo.Element} The new node
4489      */
4490     insertFirst : function(el, o, returnElement){
4491         return this.doInsert(el, o, returnElement, "afterBegin");
4492     },
4493
4494     // private
4495     doInsert : function(el, o, returnElement, pos, sibling){
4496         el = Roo.getDom(el);
4497         var newNode;
4498         if(this.useDom || o.ns){
4499             newNode = createDom(o, null);
4500             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4501         }else{
4502             var html = createHtml(o);
4503             newNode = this.insertHtml(pos, el, html);
4504         }
4505         return returnElement ? Roo.get(newNode, true) : newNode;
4506     },
4507
4508     /**
4509      * Creates new Dom element(s) and appends them to el
4510      * @param {String/HTMLElement/Element} el The context element
4511      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4512      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4513      * @return {HTMLElement/Roo.Element} The new node
4514      */
4515     append : function(el, o, returnElement){
4516         el = Roo.getDom(el);
4517         var newNode;
4518         if(this.useDom || o.ns){
4519             newNode = createDom(o, null);
4520             el.appendChild(newNode);
4521         }else{
4522             var html = createHtml(o);
4523             newNode = this.insertHtml("beforeEnd", el, html);
4524         }
4525         return returnElement ? Roo.get(newNode, true) : newNode;
4526     },
4527
4528     /**
4529      * Creates new Dom element(s) and overwrites the contents of el with them
4530      * @param {String/HTMLElement/Element} el The context element
4531      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4532      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4533      * @return {HTMLElement/Roo.Element} The new node
4534      */
4535     overwrite : function(el, o, returnElement){
4536         el = Roo.getDom(el);
4537         if (o.ns) {
4538           
4539             while (el.childNodes.length) {
4540                 el.removeChild(el.firstChild);
4541             }
4542             createDom(o, el);
4543         } else {
4544             el.innerHTML = createHtml(o);   
4545         }
4546         
4547         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4548     },
4549
4550     /**
4551      * Creates a new Roo.DomHelper.Template from the Dom object spec
4552      * @param {Object} o The Dom object spec (and children)
4553      * @return {Roo.DomHelper.Template} The new template
4554      */
4555     createTemplate : function(o){
4556         var html = createHtml(o);
4557         return new Roo.Template(html);
4558     }
4559     };
4560 }();
4561 /*
4562  * Based on:
4563  * Ext JS Library 1.1.1
4564  * Copyright(c) 2006-2007, Ext JS, LLC.
4565  *
4566  * Originally Released Under LGPL - original licence link has changed is not relivant.
4567  *
4568  * Fork - LGPL
4569  * <script type="text/javascript">
4570  */
4571  
4572 /**
4573 * @class Roo.Template
4574 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4575 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4576 * Usage:
4577 <pre><code>
4578 var t = new Roo.Template({
4579     html :  '&lt;div name="{id}"&gt;' + 
4580         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4581         '&lt;/div&gt;',
4582     myformat: function (value, allValues) {
4583         return 'XX' + value;
4584     }
4585 });
4586 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4587 </code></pre>
4588 * For more information see this blog post with examples:
4589 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4590      - Create Elements using DOM, HTML fragments and Templates</a>. 
4591 * @constructor
4592 * @param {Object} cfg - Configuration object.
4593 */
4594 Roo.Template = function(cfg){
4595     // BC!
4596     if(cfg instanceof Array){
4597         cfg = cfg.join("");
4598     }else if(arguments.length > 1){
4599         cfg = Array.prototype.join.call(arguments, "");
4600     }
4601     
4602     
4603     if (typeof(cfg) == 'object') {
4604         Roo.apply(this,cfg)
4605     } else {
4606         // bc
4607         this.html = cfg;
4608     }
4609     if (this.url) {
4610         this.load();
4611     }
4612     
4613 };
4614 Roo.Template.prototype = {
4615     
4616     /**
4617      * @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..
4618      *                    it should be fixed so that template is observable...
4619      */
4620     url : false,
4621     /**
4622      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4623      */
4624     html : '',
4625     /**
4626      * Returns an HTML fragment of this template with the specified values applied.
4627      * @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'})
4628      * @return {String} The HTML fragment
4629      */
4630     applyTemplate : function(values){
4631         try {
4632            
4633             if(this.compiled){
4634                 return this.compiled(values);
4635             }
4636             var useF = this.disableFormats !== true;
4637             var fm = Roo.util.Format, tpl = this;
4638             var fn = function(m, name, format, args){
4639                 if(format && useF){
4640                     if(format.substr(0, 5) == "this."){
4641                         return tpl.call(format.substr(5), values[name], values);
4642                     }else{
4643                         if(args){
4644                             // quoted values are required for strings in compiled templates, 
4645                             // but for non compiled we need to strip them
4646                             // quoted reversed for jsmin
4647                             var re = /^\s*['"](.*)["']\s*$/;
4648                             args = args.split(',');
4649                             for(var i = 0, len = args.length; i < len; i++){
4650                                 args[i] = args[i].replace(re, "$1");
4651                             }
4652                             args = [values[name]].concat(args);
4653                         }else{
4654                             args = [values[name]];
4655                         }
4656                         return fm[format].apply(fm, args);
4657                     }
4658                 }else{
4659                     return values[name] !== undefined ? values[name] : "";
4660                 }
4661             };
4662             return this.html.replace(this.re, fn);
4663         } catch (e) {
4664             Roo.log(e);
4665             throw e;
4666         }
4667          
4668     },
4669     
4670     loading : false,
4671       
4672     load : function ()
4673     {
4674          
4675         if (this.loading) {
4676             return;
4677         }
4678         var _t = this;
4679         
4680         this.loading = true;
4681         this.compiled = false;
4682         
4683         var cx = new Roo.data.Connection();
4684         cx.request({
4685             url : this.url,
4686             method : 'GET',
4687             success : function (response) {
4688                 _t.loading = false;
4689                 _t.html = response.responseText;
4690                 _t.url = false;
4691                 _t.compile();
4692              },
4693             failure : function(response) {
4694                 Roo.log("Template failed to load from " + _t.url);
4695                 _t.loading = false;
4696             }
4697         });
4698     },
4699
4700     /**
4701      * Sets the HTML used as the template and optionally compiles it.
4702      * @param {String} html
4703      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4704      * @return {Roo.Template} this
4705      */
4706     set : function(html, compile){
4707         this.html = html;
4708         this.compiled = null;
4709         if(compile){
4710             this.compile();
4711         }
4712         return this;
4713     },
4714     
4715     /**
4716      * True to disable format functions (defaults to false)
4717      * @type Boolean
4718      */
4719     disableFormats : false,
4720     
4721     /**
4722     * The regular expression used to match template variables 
4723     * @type RegExp
4724     * @property 
4725     */
4726     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4727     
4728     /**
4729      * Compiles the template into an internal function, eliminating the RegEx overhead.
4730      * @return {Roo.Template} this
4731      */
4732     compile : function(){
4733         var fm = Roo.util.Format;
4734         var useF = this.disableFormats !== true;
4735         var sep = Roo.isGecko ? "+" : ",";
4736         var fn = function(m, name, format, args){
4737             if(format && useF){
4738                 args = args ? ',' + args : "";
4739                 if(format.substr(0, 5) != "this."){
4740                     format = "fm." + format + '(';
4741                 }else{
4742                     format = 'this.call("'+ format.substr(5) + '", ';
4743                     args = ", values";
4744                 }
4745             }else{
4746                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4747             }
4748             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4749         };
4750         var body;
4751         // branched to use + in gecko and [].join() in others
4752         if(Roo.isGecko){
4753             body = "this.compiled = function(values){ return '" +
4754                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4755                     "';};";
4756         }else{
4757             body = ["this.compiled = function(values){ return ['"];
4758             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4759             body.push("'].join('');};");
4760             body = body.join('');
4761         }
4762         /**
4763          * eval:var:values
4764          * eval:var:fm
4765          */
4766         eval(body);
4767         return this;
4768     },
4769     
4770     // private function used to call members
4771     call : function(fnName, value, allValues){
4772         return this[fnName](value, allValues);
4773     },
4774     
4775     /**
4776      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4777      * @param {String/HTMLElement/Roo.Element} el The context element
4778      * @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'})
4779      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4780      * @return {HTMLElement/Roo.Element} The new node or Element
4781      */
4782     insertFirst: function(el, values, returnElement){
4783         return this.doInsert('afterBegin', el, values, returnElement);
4784     },
4785
4786     /**
4787      * Applies the supplied values to the template and inserts the new node(s) before el.
4788      * @param {String/HTMLElement/Roo.Element} el The context element
4789      * @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'})
4790      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4791      * @return {HTMLElement/Roo.Element} The new node or Element
4792      */
4793     insertBefore: function(el, values, returnElement){
4794         return this.doInsert('beforeBegin', el, values, returnElement);
4795     },
4796
4797     /**
4798      * Applies the supplied values to the template and inserts the new node(s) after el.
4799      * @param {String/HTMLElement/Roo.Element} el The context element
4800      * @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'})
4801      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4802      * @return {HTMLElement/Roo.Element} The new node or Element
4803      */
4804     insertAfter : function(el, values, returnElement){
4805         return this.doInsert('afterEnd', el, values, returnElement);
4806     },
4807     
4808     /**
4809      * Applies the supplied values to the template and appends the new node(s) to el.
4810      * @param {String/HTMLElement/Roo.Element} el The context element
4811      * @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'})
4812      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4813      * @return {HTMLElement/Roo.Element} The new node or Element
4814      */
4815     append : function(el, values, returnElement){
4816         return this.doInsert('beforeEnd', el, values, returnElement);
4817     },
4818
4819     doInsert : function(where, el, values, returnEl){
4820         el = Roo.getDom(el);
4821         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4822         return returnEl ? Roo.get(newNode, true) : newNode;
4823     },
4824
4825     /**
4826      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4827      * @param {String/HTMLElement/Roo.Element} el The context element
4828      * @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'})
4829      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4830      * @return {HTMLElement/Roo.Element} The new node or Element
4831      */
4832     overwrite : function(el, values, returnElement){
4833         el = Roo.getDom(el);
4834         el.innerHTML = this.applyTemplate(values);
4835         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4836     }
4837 };
4838 /**
4839  * Alias for {@link #applyTemplate}
4840  * @method
4841  */
4842 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4843
4844 // backwards compat
4845 Roo.DomHelper.Template = Roo.Template;
4846
4847 /**
4848  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4849  * @param {String/HTMLElement} el A DOM element or its id
4850  * @returns {Roo.Template} The created template
4851  * @static
4852  */
4853 Roo.Template.from = function(el){
4854     el = Roo.getDom(el);
4855     return new Roo.Template(el.value || el.innerHTML);
4856 };/*
4857  * Based on:
4858  * Ext JS Library 1.1.1
4859  * Copyright(c) 2006-2007, Ext JS, LLC.
4860  *
4861  * Originally Released Under LGPL - original licence link has changed is not relivant.
4862  *
4863  * Fork - LGPL
4864  * <script type="text/javascript">
4865  */
4866  
4867
4868 /*
4869  * This is code is also distributed under MIT license for use
4870  * with jQuery and prototype JavaScript libraries.
4871  */
4872 /**
4873  * @class Roo.DomQuery
4874 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).
4875 <p>
4876 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>
4877
4878 <p>
4879 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.
4880 </p>
4881 <h4>Element Selectors:</h4>
4882 <ul class="list">
4883     <li> <b>*</b> any element</li>
4884     <li> <b>E</b> an element with the tag E</li>
4885     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4886     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4887     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4888     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4889 </ul>
4890 <h4>Attribute Selectors:</h4>
4891 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4892 <ul class="list">
4893     <li> <b>E[foo]</b> has an attribute "foo"</li>
4894     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4895     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4896     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4897     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4898     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4899     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4900 </ul>
4901 <h4>Pseudo Classes:</h4>
4902 <ul class="list">
4903     <li> <b>E:first-child</b> E is the first child of its parent</li>
4904     <li> <b>E:last-child</b> E is the last child of its parent</li>
4905     <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>
4906     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4907     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4908     <li> <b>E:only-child</b> E is the only child of its parent</li>
4909     <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>
4910     <li> <b>E:first</b> the first E in the resultset</li>
4911     <li> <b>E:last</b> the last E in the resultset</li>
4912     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4913     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4914     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4915     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4916     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4917     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4918     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4919     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4920     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4921 </ul>
4922 <h4>CSS Value Selectors:</h4>
4923 <ul class="list">
4924     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4925     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4926     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4927     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4928     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4929     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4930 </ul>
4931  * @singleton
4932  */
4933 Roo.DomQuery = function(){
4934     var cache = {}, simpleCache = {}, valueCache = {};
4935     var nonSpace = /\S/;
4936     var trimRe = /^\s+|\s+$/g;
4937     var tplRe = /\{(\d+)\}/g;
4938     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4939     var tagTokenRe = /^(#)?([\w-\*]+)/;
4940     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4941
4942     function child(p, index){
4943         var i = 0;
4944         var n = p.firstChild;
4945         while(n){
4946             if(n.nodeType == 1){
4947                if(++i == index){
4948                    return n;
4949                }
4950             }
4951             n = n.nextSibling;
4952         }
4953         return null;
4954     };
4955
4956     function next(n){
4957         while((n = n.nextSibling) && n.nodeType != 1);
4958         return n;
4959     };
4960
4961     function prev(n){
4962         while((n = n.previousSibling) && n.nodeType != 1);
4963         return n;
4964     };
4965
4966     function children(d){
4967         var n = d.firstChild, ni = -1;
4968             while(n){
4969                 var nx = n.nextSibling;
4970                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4971                     d.removeChild(n);
4972                 }else{
4973                     n.nodeIndex = ++ni;
4974                 }
4975                 n = nx;
4976             }
4977             return this;
4978         };
4979
4980     function byClassName(c, a, v){
4981         if(!v){
4982             return c;
4983         }
4984         var r = [], ri = -1, cn;
4985         for(var i = 0, ci; ci = c[i]; i++){
4986             if((' '+ci.className+' ').indexOf(v) != -1){
4987                 r[++ri] = ci;
4988             }
4989         }
4990         return r;
4991     };
4992
4993     function attrValue(n, attr){
4994         if(!n.tagName && typeof n.length != "undefined"){
4995             n = n[0];
4996         }
4997         if(!n){
4998             return null;
4999         }
5000         if(attr == "for"){
5001             return n.htmlFor;
5002         }
5003         if(attr == "class" || attr == "className"){
5004             return n.className;
5005         }
5006         return n.getAttribute(attr) || n[attr];
5007
5008     };
5009
5010     function getNodes(ns, mode, tagName){
5011         var result = [], ri = -1, cs;
5012         if(!ns){
5013             return result;
5014         }
5015         tagName = tagName || "*";
5016         if(typeof ns.getElementsByTagName != "undefined"){
5017             ns = [ns];
5018         }
5019         if(!mode){
5020             for(var i = 0, ni; ni = ns[i]; i++){
5021                 cs = ni.getElementsByTagName(tagName);
5022                 for(var j = 0, ci; ci = cs[j]; j++){
5023                     result[++ri] = ci;
5024                 }
5025             }
5026         }else if(mode == "/" || mode == ">"){
5027             var utag = tagName.toUpperCase();
5028             for(var i = 0, ni, cn; ni = ns[i]; i++){
5029                 cn = ni.children || ni.childNodes;
5030                 for(var j = 0, cj; cj = cn[j]; j++){
5031                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5032                         result[++ri] = cj;
5033                     }
5034                 }
5035             }
5036         }else if(mode == "+"){
5037             var utag = tagName.toUpperCase();
5038             for(var i = 0, n; n = ns[i]; i++){
5039                 while((n = n.nextSibling) && n.nodeType != 1);
5040                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5041                     result[++ri] = n;
5042                 }
5043             }
5044         }else if(mode == "~"){
5045             for(var i = 0, n; n = ns[i]; i++){
5046                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5047                 if(n){
5048                     result[++ri] = n;
5049                 }
5050             }
5051         }
5052         return result;
5053     };
5054
5055     function concat(a, b){
5056         if(b.slice){
5057             return a.concat(b);
5058         }
5059         for(var i = 0, l = b.length; i < l; i++){
5060             a[a.length] = b[i];
5061         }
5062         return a;
5063     }
5064
5065     function byTag(cs, tagName){
5066         if(cs.tagName || cs == document){
5067             cs = [cs];
5068         }
5069         if(!tagName){
5070             return cs;
5071         }
5072         var r = [], ri = -1;
5073         tagName = tagName.toLowerCase();
5074         for(var i = 0, ci; ci = cs[i]; i++){
5075             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5076                 r[++ri] = ci;
5077             }
5078         }
5079         return r;
5080     };
5081
5082     function byId(cs, attr, id){
5083         if(cs.tagName || cs == document){
5084             cs = [cs];
5085         }
5086         if(!id){
5087             return cs;
5088         }
5089         var r = [], ri = -1;
5090         for(var i = 0,ci; ci = cs[i]; i++){
5091             if(ci && ci.id == id){
5092                 r[++ri] = ci;
5093                 return r;
5094             }
5095         }
5096         return r;
5097     };
5098
5099     function byAttribute(cs, attr, value, op, custom){
5100         var r = [], ri = -1, st = custom=="{";
5101         var f = Roo.DomQuery.operators[op];
5102         for(var i = 0, ci; ci = cs[i]; i++){
5103             var a;
5104             if(st){
5105                 a = Roo.DomQuery.getStyle(ci, attr);
5106             }
5107             else if(attr == "class" || attr == "className"){
5108                 a = ci.className;
5109             }else if(attr == "for"){
5110                 a = ci.htmlFor;
5111             }else if(attr == "href"){
5112                 a = ci.getAttribute("href", 2);
5113             }else{
5114                 a = ci.getAttribute(attr);
5115             }
5116             if((f && f(a, value)) || (!f && a)){
5117                 r[++ri] = ci;
5118             }
5119         }
5120         return r;
5121     };
5122
5123     function byPseudo(cs, name, value){
5124         return Roo.DomQuery.pseudos[name](cs, value);
5125     };
5126
5127     // This is for IE MSXML which does not support expandos.
5128     // IE runs the same speed using setAttribute, however FF slows way down
5129     // and Safari completely fails so they need to continue to use expandos.
5130     var isIE = window.ActiveXObject ? true : false;
5131
5132     // this eval is stop the compressor from
5133     // renaming the variable to something shorter
5134     
5135     /** eval:var:batch */
5136     var batch = 30803; 
5137
5138     var key = 30803;
5139
5140     function nodupIEXml(cs){
5141         var d = ++key;
5142         cs[0].setAttribute("_nodup", d);
5143         var r = [cs[0]];
5144         for(var i = 1, len = cs.length; i < len; i++){
5145             var c = cs[i];
5146             if(!c.getAttribute("_nodup") != d){
5147                 c.setAttribute("_nodup", d);
5148                 r[r.length] = c;
5149             }
5150         }
5151         for(var i = 0, len = cs.length; i < len; i++){
5152             cs[i].removeAttribute("_nodup");
5153         }
5154         return r;
5155     }
5156
5157     function nodup(cs){
5158         if(!cs){
5159             return [];
5160         }
5161         var len = cs.length, c, i, r = cs, cj, ri = -1;
5162         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5163             return cs;
5164         }
5165         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5166             return nodupIEXml(cs);
5167         }
5168         var d = ++key;
5169         cs[0]._nodup = d;
5170         for(i = 1; c = cs[i]; i++){
5171             if(c._nodup != d){
5172                 c._nodup = d;
5173             }else{
5174                 r = [];
5175                 for(var j = 0; j < i; j++){
5176                     r[++ri] = cs[j];
5177                 }
5178                 for(j = i+1; cj = cs[j]; j++){
5179                     if(cj._nodup != d){
5180                         cj._nodup = d;
5181                         r[++ri] = cj;
5182                     }
5183                 }
5184                 return r;
5185             }
5186         }
5187         return r;
5188     }
5189
5190     function quickDiffIEXml(c1, c2){
5191         var d = ++key;
5192         for(var i = 0, len = c1.length; i < len; i++){
5193             c1[i].setAttribute("_qdiff", d);
5194         }
5195         var r = [];
5196         for(var i = 0, len = c2.length; i < len; i++){
5197             if(c2[i].getAttribute("_qdiff") != d){
5198                 r[r.length] = c2[i];
5199             }
5200         }
5201         for(var i = 0, len = c1.length; i < len; i++){
5202            c1[i].removeAttribute("_qdiff");
5203         }
5204         return r;
5205     }
5206
5207     function quickDiff(c1, c2){
5208         var len1 = c1.length;
5209         if(!len1){
5210             return c2;
5211         }
5212         if(isIE && c1[0].selectSingleNode){
5213             return quickDiffIEXml(c1, c2);
5214         }
5215         var d = ++key;
5216         for(var i = 0; i < len1; i++){
5217             c1[i]._qdiff = d;
5218         }
5219         var r = [];
5220         for(var i = 0, len = c2.length; i < len; i++){
5221             if(c2[i]._qdiff != d){
5222                 r[r.length] = c2[i];
5223             }
5224         }
5225         return r;
5226     }
5227
5228     function quickId(ns, mode, root, id){
5229         if(ns == root){
5230            var d = root.ownerDocument || root;
5231            return d.getElementById(id);
5232         }
5233         ns = getNodes(ns, mode, "*");
5234         return byId(ns, null, id);
5235     }
5236
5237     return {
5238         getStyle : function(el, name){
5239             return Roo.fly(el).getStyle(name);
5240         },
5241         /**
5242          * Compiles a selector/xpath query into a reusable function. The returned function
5243          * takes one parameter "root" (optional), which is the context node from where the query should start.
5244          * @param {String} selector The selector/xpath query
5245          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5246          * @return {Function}
5247          */
5248         compile : function(path, type){
5249             type = type || "select";
5250             
5251             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5252             var q = path, mode, lq;
5253             var tk = Roo.DomQuery.matchers;
5254             var tklen = tk.length;
5255             var mm;
5256
5257             // accept leading mode switch
5258             var lmode = q.match(modeRe);
5259             if(lmode && lmode[1]){
5260                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5261                 q = q.replace(lmode[1], "");
5262             }
5263             // strip leading slashes
5264             while(path.substr(0, 1)=="/"){
5265                 path = path.substr(1);
5266             }
5267
5268             while(q && lq != q){
5269                 lq = q;
5270                 var tm = q.match(tagTokenRe);
5271                 if(type == "select"){
5272                     if(tm){
5273                         if(tm[1] == "#"){
5274                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5275                         }else{
5276                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5277                         }
5278                         q = q.replace(tm[0], "");
5279                     }else if(q.substr(0, 1) != '@'){
5280                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5281                     }
5282                 }else{
5283                     if(tm){
5284                         if(tm[1] == "#"){
5285                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5286                         }else{
5287                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5288                         }
5289                         q = q.replace(tm[0], "");
5290                     }
5291                 }
5292                 while(!(mm = q.match(modeRe))){
5293                     var matched = false;
5294                     for(var j = 0; j < tklen; j++){
5295                         var t = tk[j];
5296                         var m = q.match(t.re);
5297                         if(m){
5298                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5299                                                     return m[i];
5300                                                 });
5301                             q = q.replace(m[0], "");
5302                             matched = true;
5303                             break;
5304                         }
5305                     }
5306                     // prevent infinite loop on bad selector
5307                     if(!matched){
5308                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5309                     }
5310                 }
5311                 if(mm[1]){
5312                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5313                     q = q.replace(mm[1], "");
5314                 }
5315             }
5316             fn[fn.length] = "return nodup(n);\n}";
5317             
5318              /** 
5319               * list of variables that need from compression as they are used by eval.
5320              *  eval:var:batch 
5321              *  eval:var:nodup
5322              *  eval:var:byTag
5323              *  eval:var:ById
5324              *  eval:var:getNodes
5325              *  eval:var:quickId
5326              *  eval:var:mode
5327              *  eval:var:root
5328              *  eval:var:n
5329              *  eval:var:byClassName
5330              *  eval:var:byPseudo
5331              *  eval:var:byAttribute
5332              *  eval:var:attrValue
5333              * 
5334              **/ 
5335             eval(fn.join(""));
5336             return f;
5337         },
5338
5339         /**
5340          * Selects a group of elements.
5341          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5342          * @param {Node} root (optional) The start of the query (defaults to document).
5343          * @return {Array}
5344          */
5345         select : function(path, root, type){
5346             if(!root || root == document){
5347                 root = document;
5348             }
5349             if(typeof root == "string"){
5350                 root = document.getElementById(root);
5351             }
5352             var paths = path.split(",");
5353             var results = [];
5354             for(var i = 0, len = paths.length; i < len; i++){
5355                 var p = paths[i].replace(trimRe, "");
5356                 if(!cache[p]){
5357                     cache[p] = Roo.DomQuery.compile(p);
5358                     if(!cache[p]){
5359                         throw p + " is not a valid selector";
5360                     }
5361                 }
5362                 var result = cache[p](root);
5363                 if(result && result != document){
5364                     results = results.concat(result);
5365                 }
5366             }
5367             if(paths.length > 1){
5368                 return nodup(results);
5369             }
5370             return results;
5371         },
5372
5373         /**
5374          * Selects a single element.
5375          * @param {String} selector The selector/xpath query
5376          * @param {Node} root (optional) The start of the query (defaults to document).
5377          * @return {Element}
5378          */
5379         selectNode : function(path, root){
5380             return Roo.DomQuery.select(path, root)[0];
5381         },
5382
5383         /**
5384          * Selects the value of a node, optionally replacing null with the defaultValue.
5385          * @param {String} selector The selector/xpath query
5386          * @param {Node} root (optional) The start of the query (defaults to document).
5387          * @param {String} defaultValue
5388          */
5389         selectValue : function(path, root, defaultValue){
5390             path = path.replace(trimRe, "");
5391             if(!valueCache[path]){
5392                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5393             }
5394             var n = valueCache[path](root);
5395             n = n[0] ? n[0] : n;
5396             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5397             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5398         },
5399
5400         /**
5401          * Selects the value of a node, parsing integers and floats.
5402          * @param {String} selector The selector/xpath query
5403          * @param {Node} root (optional) The start of the query (defaults to document).
5404          * @param {Number} defaultValue
5405          * @return {Number}
5406          */
5407         selectNumber : function(path, root, defaultValue){
5408             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5409             return parseFloat(v);
5410         },
5411
5412         /**
5413          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5414          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5415          * @param {String} selector The simple selector to test
5416          * @return {Boolean}
5417          */
5418         is : function(el, ss){
5419             if(typeof el == "string"){
5420                 el = document.getElementById(el);
5421             }
5422             var isArray = (el instanceof Array);
5423             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5424             return isArray ? (result.length == el.length) : (result.length > 0);
5425         },
5426
5427         /**
5428          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5429          * @param {Array} el An array of elements to filter
5430          * @param {String} selector The simple selector to test
5431          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5432          * the selector instead of the ones that match
5433          * @return {Array}
5434          */
5435         filter : function(els, ss, nonMatches){
5436             ss = ss.replace(trimRe, "");
5437             if(!simpleCache[ss]){
5438                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5439             }
5440             var result = simpleCache[ss](els);
5441             return nonMatches ? quickDiff(result, els) : result;
5442         },
5443
5444         /**
5445          * Collection of matching regular expressions and code snippets.
5446          */
5447         matchers : [{
5448                 re: /^\.([\w-]+)/,
5449                 select: 'n = byClassName(n, null, " {1} ");'
5450             }, {
5451                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5452                 select: 'n = byPseudo(n, "{1}", "{2}");'
5453             },{
5454                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5455                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5456             }, {
5457                 re: /^#([\w-]+)/,
5458                 select: 'n = byId(n, null, "{1}");'
5459             },{
5460                 re: /^@([\w-]+)/,
5461                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5462             }
5463         ],
5464
5465         /**
5466          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5467          * 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;.
5468          */
5469         operators : {
5470             "=" : function(a, v){
5471                 return a == v;
5472             },
5473             "!=" : function(a, v){
5474                 return a != v;
5475             },
5476             "^=" : function(a, v){
5477                 return a && a.substr(0, v.length) == v;
5478             },
5479             "$=" : function(a, v){
5480                 return a && a.substr(a.length-v.length) == v;
5481             },
5482             "*=" : function(a, v){
5483                 return a && a.indexOf(v) !== -1;
5484             },
5485             "%=" : function(a, v){
5486                 return (a % v) == 0;
5487             },
5488             "|=" : function(a, v){
5489                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5490             },
5491             "~=" : function(a, v){
5492                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5493             }
5494         },
5495
5496         /**
5497          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5498          * and the argument (if any) supplied in the selector.
5499          */
5500         pseudos : {
5501             "first-child" : function(c){
5502                 var r = [], ri = -1, n;
5503                 for(var i = 0, ci; ci = n = c[i]; i++){
5504                     while((n = n.previousSibling) && n.nodeType != 1);
5505                     if(!n){
5506                         r[++ri] = ci;
5507                     }
5508                 }
5509                 return r;
5510             },
5511
5512             "last-child" : function(c){
5513                 var r = [], ri = -1, n;
5514                 for(var i = 0, ci; ci = n = c[i]; i++){
5515                     while((n = n.nextSibling) && n.nodeType != 1);
5516                     if(!n){
5517                         r[++ri] = ci;
5518                     }
5519                 }
5520                 return r;
5521             },
5522
5523             "nth-child" : function(c, a) {
5524                 var r = [], ri = -1;
5525                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5526                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5527                 for(var i = 0, n; n = c[i]; i++){
5528                     var pn = n.parentNode;
5529                     if (batch != pn._batch) {
5530                         var j = 0;
5531                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5532                             if(cn.nodeType == 1){
5533                                cn.nodeIndex = ++j;
5534                             }
5535                         }
5536                         pn._batch = batch;
5537                     }
5538                     if (f == 1) {
5539                         if (l == 0 || n.nodeIndex == l){
5540                             r[++ri] = n;
5541                         }
5542                     } else if ((n.nodeIndex + l) % f == 0){
5543                         r[++ri] = n;
5544                     }
5545                 }
5546
5547                 return r;
5548             },
5549
5550             "only-child" : function(c){
5551                 var r = [], ri = -1;;
5552                 for(var i = 0, ci; ci = c[i]; i++){
5553                     if(!prev(ci) && !next(ci)){
5554                         r[++ri] = ci;
5555                     }
5556                 }
5557                 return r;
5558             },
5559
5560             "empty" : function(c){
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     var cns = ci.childNodes, j = 0, cn, empty = true;
5564                     while(cn = cns[j]){
5565                         ++j;
5566                         if(cn.nodeType == 1 || cn.nodeType == 3){
5567                             empty = false;
5568                             break;
5569                         }
5570                     }
5571                     if(empty){
5572                         r[++ri] = ci;
5573                     }
5574                 }
5575                 return r;
5576             },
5577
5578             "contains" : function(c, v){
5579                 var r = [], ri = -1;
5580                 for(var i = 0, ci; ci = c[i]; i++){
5581                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5582                         r[++ri] = ci;
5583                     }
5584                 }
5585                 return r;
5586             },
5587
5588             "nodeValue" : function(c, v){
5589                 var r = [], ri = -1;
5590                 for(var i = 0, ci; ci = c[i]; i++){
5591                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5592                         r[++ri] = ci;
5593                     }
5594                 }
5595                 return r;
5596             },
5597
5598             "checked" : function(c){
5599                 var r = [], ri = -1;
5600                 for(var i = 0, ci; ci = c[i]; i++){
5601                     if(ci.checked == true){
5602                         r[++ri] = ci;
5603                     }
5604                 }
5605                 return r;
5606             },
5607
5608             "not" : function(c, ss){
5609                 return Roo.DomQuery.filter(c, ss, true);
5610             },
5611
5612             "odd" : function(c){
5613                 return this["nth-child"](c, "odd");
5614             },
5615
5616             "even" : function(c){
5617                 return this["nth-child"](c, "even");
5618             },
5619
5620             "nth" : function(c, a){
5621                 return c[a-1] || [];
5622             },
5623
5624             "first" : function(c){
5625                 return c[0] || [];
5626             },
5627
5628             "last" : function(c){
5629                 return c[c.length-1] || [];
5630             },
5631
5632             "has" : function(c, ss){
5633                 var s = Roo.DomQuery.select;
5634                 var r = [], ri = -1;
5635                 for(var i = 0, ci; ci = c[i]; i++){
5636                     if(s(ss, ci).length > 0){
5637                         r[++ri] = ci;
5638                     }
5639                 }
5640                 return r;
5641             },
5642
5643             "next" : function(c, ss){
5644                 var is = Roo.DomQuery.is;
5645                 var r = [], ri = -1;
5646                 for(var i = 0, ci; ci = c[i]; i++){
5647                     var n = next(ci);
5648                     if(n && is(n, ss)){
5649                         r[++ri] = ci;
5650                     }
5651                 }
5652                 return r;
5653             },
5654
5655             "prev" : function(c, ss){
5656                 var is = Roo.DomQuery.is;
5657                 var r = [], ri = -1;
5658                 for(var i = 0, ci; ci = c[i]; i++){
5659                     var n = prev(ci);
5660                     if(n && is(n, ss)){
5661                         r[++ri] = ci;
5662                     }
5663                 }
5664                 return r;
5665             }
5666         }
5667     };
5668 }();
5669
5670 /**
5671  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5672  * @param {String} path The selector/xpath query
5673  * @param {Node} root (optional) The start of the query (defaults to document).
5674  * @return {Array}
5675  * @member Roo
5676  * @method query
5677  */
5678 Roo.query = Roo.DomQuery.select;
5679 /*
5680  * Based on:
5681  * Ext JS Library 1.1.1
5682  * Copyright(c) 2006-2007, Ext JS, LLC.
5683  *
5684  * Originally Released Under LGPL - original licence link has changed is not relivant.
5685  *
5686  * Fork - LGPL
5687  * <script type="text/javascript">
5688  */
5689
5690 /**
5691  * @class Roo.util.Observable
5692  * Base class that provides a common interface for publishing events. Subclasses are expected to
5693  * to have a property "events" with all the events defined.<br>
5694  * For example:
5695  * <pre><code>
5696  Employee = function(name){
5697     this.name = name;
5698     this.addEvents({
5699         "fired" : true,
5700         "quit" : true
5701     });
5702  }
5703  Roo.extend(Employee, Roo.util.Observable);
5704 </code></pre>
5705  * @param {Object} config properties to use (incuding events / listeners)
5706  */
5707
5708 Roo.util.Observable = function(cfg){
5709     
5710     cfg = cfg|| {};
5711     this.addEvents(cfg.events || {});
5712     if (cfg.events) {
5713         delete cfg.events; // make sure
5714     }
5715      
5716     Roo.apply(this, cfg);
5717     
5718     if(this.listeners){
5719         this.on(this.listeners);
5720         delete this.listeners;
5721     }
5722 };
5723 Roo.util.Observable.prototype = {
5724     /** 
5725  * @cfg {Object} listeners  list of events and functions to call for this object, 
5726  * For example :
5727  * <pre><code>
5728     listeners :  { 
5729        'click' : function(e) {
5730            ..... 
5731         } ,
5732         .... 
5733     } 
5734   </code></pre>
5735  */
5736     
5737     
5738     /**
5739      * Fires the specified event with the passed parameters (minus the event name).
5740      * @param {String} eventName
5741      * @param {Object...} args Variable number of parameters are passed to handlers
5742      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5743      */
5744     fireEvent : function(){
5745         var ce = this.events[arguments[0].toLowerCase()];
5746         if(typeof ce == "object"){
5747             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5748         }else{
5749             return true;
5750         }
5751     },
5752
5753     // private
5754     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5755
5756     /**
5757      * Appends an event handler to this component
5758      * @param {String}   eventName The type of event to listen for
5759      * @param {Function} handler The method the event invokes
5760      * @param {Object}   scope (optional) The scope in which to execute the handler
5761      * function. The handler function's "this" context.
5762      * @param {Object}   options (optional) An object containing handler configuration
5763      * properties. This may contain any of the following properties:<ul>
5764      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5765      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5766      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5767      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5768      * by the specified number of milliseconds. If the event fires again within that time, the original
5769      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5770      * </ul><br>
5771      * <p>
5772      * <b>Combining Options</b><br>
5773      * Using the options argument, it is possible to combine different types of listeners:<br>
5774      * <br>
5775      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5776                 <pre><code>
5777                 el.on('click', this.onClick, this, {
5778                         single: true,
5779                 delay: 100,
5780                 forumId: 4
5781                 });
5782                 </code></pre>
5783      * <p>
5784      * <b>Attaching multiple handlers in 1 call</b><br>
5785      * The method also allows for a single argument to be passed which is a config object containing properties
5786      * which specify multiple handlers.
5787      * <pre><code>
5788                 el.on({
5789                         'click': {
5790                         fn: this.onClick,
5791                         scope: this,
5792                         delay: 100
5793                 }, 
5794                 'mouseover': {
5795                         fn: this.onMouseOver,
5796                         scope: this
5797                 },
5798                 'mouseout': {
5799                         fn: this.onMouseOut,
5800                         scope: this
5801                 }
5802                 });
5803                 </code></pre>
5804      * <p>
5805      * Or a shorthand syntax which passes the same scope object to all handlers:
5806         <pre><code>
5807                 el.on({
5808                         'click': this.onClick,
5809                 'mouseover': this.onMouseOver,
5810                 'mouseout': this.onMouseOut,
5811                 scope: this
5812                 });
5813                 </code></pre>
5814      */
5815     addListener : function(eventName, fn, scope, o){
5816         if(typeof eventName == "object"){
5817             o = eventName;
5818             for(var e in o){
5819                 if(this.filterOptRe.test(e)){
5820                     continue;
5821                 }
5822                 if(typeof o[e] == "function"){
5823                     // shared options
5824                     this.addListener(e, o[e], o.scope,  o);
5825                 }else{
5826                     // individual options
5827                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5828                 }
5829             }
5830             return;
5831         }
5832         o = (!o || typeof o == "boolean") ? {} : o;
5833         eventName = eventName.toLowerCase();
5834         var ce = this.events[eventName] || true;
5835         if(typeof ce == "boolean"){
5836             ce = new Roo.util.Event(this, eventName);
5837             this.events[eventName] = ce;
5838         }
5839         ce.addListener(fn, scope, o);
5840     },
5841
5842     /**
5843      * Removes a listener
5844      * @param {String}   eventName     The type of event to listen for
5845      * @param {Function} handler        The handler to remove
5846      * @param {Object}   scope  (optional) The scope (this object) for the handler
5847      */
5848     removeListener : function(eventName, fn, scope){
5849         var ce = this.events[eventName.toLowerCase()];
5850         if(typeof ce == "object"){
5851             ce.removeListener(fn, scope);
5852         }
5853     },
5854
5855     /**
5856      * Removes all listeners for this object
5857      */
5858     purgeListeners : function(){
5859         for(var evt in this.events){
5860             if(typeof this.events[evt] == "object"){
5861                  this.events[evt].clearListeners();
5862             }
5863         }
5864     },
5865
5866     relayEvents : function(o, events){
5867         var createHandler = function(ename){
5868             return function(){
5869                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5870             };
5871         };
5872         for(var i = 0, len = events.length; i < len; i++){
5873             var ename = events[i];
5874             if(!this.events[ename]){ this.events[ename] = true; };
5875             o.on(ename, createHandler(ename), this);
5876         }
5877     },
5878
5879     /**
5880      * Used to define events on this Observable
5881      * @param {Object} object The object with the events defined
5882      */
5883     addEvents : function(o){
5884         if(!this.events){
5885             this.events = {};
5886         }
5887         Roo.applyIf(this.events, o);
5888     },
5889
5890     /**
5891      * Checks to see if this object has any listeners for a specified event
5892      * @param {String} eventName The name of the event to check for
5893      * @return {Boolean} True if the event is being listened for, else false
5894      */
5895     hasListener : function(eventName){
5896         var e = this.events[eventName];
5897         return typeof e == "object" && e.listeners.length > 0;
5898     }
5899 };
5900 /**
5901  * Appends an event handler to this element (shorthand for addListener)
5902  * @param {String}   eventName     The type of event to listen for
5903  * @param {Function} handler        The method the event invokes
5904  * @param {Object}   scope (optional) The scope in which to execute the handler
5905  * function. The handler function's "this" context.
5906  * @param {Object}   options  (optional)
5907  * @method
5908  */
5909 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5910 /**
5911  * Removes a listener (shorthand for removeListener)
5912  * @param {String}   eventName     The type of event to listen for
5913  * @param {Function} handler        The handler to remove
5914  * @param {Object}   scope  (optional) The scope (this object) for the handler
5915  * @method
5916  */
5917 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5918
5919 /**
5920  * Starts capture on the specified Observable. All events will be passed
5921  * to the supplied function with the event name + standard signature of the event
5922  * <b>before</b> the event is fired. If the supplied function returns false,
5923  * the event will not fire.
5924  * @param {Observable} o The Observable to capture
5925  * @param {Function} fn The function to call
5926  * @param {Object} scope (optional) The scope (this object) for the fn
5927  * @static
5928  */
5929 Roo.util.Observable.capture = function(o, fn, scope){
5930     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5931 };
5932
5933 /**
5934  * Removes <b>all</b> added captures from the Observable.
5935  * @param {Observable} o The Observable to release
5936  * @static
5937  */
5938 Roo.util.Observable.releaseCapture = function(o){
5939     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5940 };
5941
5942 (function(){
5943
5944     var createBuffered = function(h, o, scope){
5945         var task = new Roo.util.DelayedTask();
5946         return function(){
5947             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5948         };
5949     };
5950
5951     var createSingle = function(h, e, fn, scope){
5952         return function(){
5953             e.removeListener(fn, scope);
5954             return h.apply(scope, arguments);
5955         };
5956     };
5957
5958     var createDelayed = function(h, o, scope){
5959         return function(){
5960             var args = Array.prototype.slice.call(arguments, 0);
5961             setTimeout(function(){
5962                 h.apply(scope, args);
5963             }, o.delay || 10);
5964         };
5965     };
5966
5967     Roo.util.Event = function(obj, name){
5968         this.name = name;
5969         this.obj = obj;
5970         this.listeners = [];
5971     };
5972
5973     Roo.util.Event.prototype = {
5974         addListener : function(fn, scope, options){
5975             var o = options || {};
5976             scope = scope || this.obj;
5977             if(!this.isListening(fn, scope)){
5978                 var l = {fn: fn, scope: scope, options: o};
5979                 var h = fn;
5980                 if(o.delay){
5981                     h = createDelayed(h, o, scope);
5982                 }
5983                 if(o.single){
5984                     h = createSingle(h, this, fn, scope);
5985                 }
5986                 if(o.buffer){
5987                     h = createBuffered(h, o, scope);
5988                 }
5989                 l.fireFn = h;
5990                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5991                     this.listeners.push(l);
5992                 }else{
5993                     this.listeners = this.listeners.slice(0);
5994                     this.listeners.push(l);
5995                 }
5996             }
5997         },
5998
5999         findListener : function(fn, scope){
6000             scope = scope || this.obj;
6001             var ls = this.listeners;
6002             for(var i = 0, len = ls.length; i < len; i++){
6003                 var l = ls[i];
6004                 if(l.fn == fn && l.scope == scope){
6005                     return i;
6006                 }
6007             }
6008             return -1;
6009         },
6010
6011         isListening : function(fn, scope){
6012             return this.findListener(fn, scope) != -1;
6013         },
6014
6015         removeListener : function(fn, scope){
6016             var index;
6017             if((index = this.findListener(fn, scope)) != -1){
6018                 if(!this.firing){
6019                     this.listeners.splice(index, 1);
6020                 }else{
6021                     this.listeners = this.listeners.slice(0);
6022                     this.listeners.splice(index, 1);
6023                 }
6024                 return true;
6025             }
6026             return false;
6027         },
6028
6029         clearListeners : function(){
6030             this.listeners = [];
6031         },
6032
6033         fire : function(){
6034             var ls = this.listeners, scope, len = ls.length;
6035             if(len > 0){
6036                 this.firing = true;
6037                 var args = Array.prototype.slice.call(arguments, 0);
6038                 for(var i = 0; i < len; i++){
6039                     var l = ls[i];
6040                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6041                         this.firing = false;
6042                         return false;
6043                     }
6044                 }
6045                 this.firing = false;
6046             }
6047             return true;
6048         }
6049     };
6050 })();/*
6051  * Based on:
6052  * Ext JS Library 1.1.1
6053  * Copyright(c) 2006-2007, Ext JS, LLC.
6054  *
6055  * Originally Released Under LGPL - original licence link has changed is not relivant.
6056  *
6057  * Fork - LGPL
6058  * <script type="text/javascript">
6059  */
6060
6061 /**
6062  * @class Roo.EventManager
6063  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6064  * several useful events directly.
6065  * See {@link Roo.EventObject} for more details on normalized event objects.
6066  * @singleton
6067  */
6068 Roo.EventManager = function(){
6069     var docReadyEvent, docReadyProcId, docReadyState = false;
6070     var resizeEvent, resizeTask, textEvent, textSize;
6071     var E = Roo.lib.Event;
6072     var D = Roo.lib.Dom;
6073
6074     
6075     
6076
6077     var fireDocReady = function(){
6078         if(!docReadyState){
6079             docReadyState = true;
6080             Roo.isReady = true;
6081             if(docReadyProcId){
6082                 clearInterval(docReadyProcId);
6083             }
6084             if(Roo.isGecko || Roo.isOpera) {
6085                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6086             }
6087             if(Roo.isIE){
6088                 var defer = document.getElementById("ie-deferred-loader");
6089                 if(defer){
6090                     defer.onreadystatechange = null;
6091                     defer.parentNode.removeChild(defer);
6092                 }
6093             }
6094             if(docReadyEvent){
6095                 docReadyEvent.fire();
6096                 docReadyEvent.clearListeners();
6097             }
6098         }
6099     };
6100     
6101     var initDocReady = function(){
6102         docReadyEvent = new Roo.util.Event();
6103         if(Roo.isGecko || Roo.isOpera) {
6104             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6105         }else if(Roo.isIE){
6106             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6107             var defer = document.getElementById("ie-deferred-loader");
6108             defer.onreadystatechange = function(){
6109                 if(this.readyState == "complete"){
6110                     fireDocReady();
6111                 }
6112             };
6113         }else if(Roo.isSafari){ 
6114             docReadyProcId = setInterval(function(){
6115                 var rs = document.readyState;
6116                 if(rs == "complete") {
6117                     fireDocReady();     
6118                  }
6119             }, 10);
6120         }
6121         // no matter what, make sure it fires on load
6122         E.on(window, "load", fireDocReady);
6123     };
6124
6125     var createBuffered = function(h, o){
6126         var task = new Roo.util.DelayedTask(h);
6127         return function(e){
6128             // create new event object impl so new events don't wipe out properties
6129             e = new Roo.EventObjectImpl(e);
6130             task.delay(o.buffer, h, null, [e]);
6131         };
6132     };
6133
6134     var createSingle = function(h, el, ename, fn){
6135         return function(e){
6136             Roo.EventManager.removeListener(el, ename, fn);
6137             h(e);
6138         };
6139     };
6140
6141     var createDelayed = function(h, o){
6142         return function(e){
6143             // create new event object impl so new events don't wipe out properties
6144             e = new Roo.EventObjectImpl(e);
6145             setTimeout(function(){
6146                 h(e);
6147             }, o.delay || 10);
6148         };
6149     };
6150     var transitionEndVal = false;
6151     
6152     var transitionEnd = function()
6153     {
6154         if (transitionEndVal) {
6155             return transitionEndVal;
6156         }
6157         var el = document.createElement('div');
6158
6159         var transEndEventNames = {
6160             WebkitTransition : 'webkitTransitionEnd',
6161             MozTransition    : 'transitionend',
6162             OTransition      : 'oTransitionEnd otransitionend',
6163             transition       : 'transitionend'
6164         };
6165     
6166         for (var name in transEndEventNames) {
6167             if (el.style[name] !== undefined) {
6168                 transitionEndVal = transEndEventNames[name];
6169                 return  transitionEndVal ;
6170             }
6171         }
6172     }
6173     
6174
6175     var listen = function(element, ename, opt, fn, scope){
6176         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6177         fn = fn || o.fn; scope = scope || o.scope;
6178         var el = Roo.getDom(element);
6179         
6180         
6181         if(!el){
6182             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6183         }
6184         
6185         if (ename == 'transitionend') {
6186             ename = transitionEnd();
6187         }
6188         var h = function(e){
6189             e = Roo.EventObject.setEvent(e);
6190             var t;
6191             if(o.delegate){
6192                 t = e.getTarget(o.delegate, el);
6193                 if(!t){
6194                     return;
6195                 }
6196             }else{
6197                 t = e.target;
6198             }
6199             if(o.stopEvent === true){
6200                 e.stopEvent();
6201             }
6202             if(o.preventDefault === true){
6203                e.preventDefault();
6204             }
6205             if(o.stopPropagation === true){
6206                 e.stopPropagation();
6207             }
6208
6209             if(o.normalized === false){
6210                 e = e.browserEvent;
6211             }
6212
6213             fn.call(scope || el, e, t, o);
6214         };
6215         if(o.delay){
6216             h = createDelayed(h, o);
6217         }
6218         if(o.single){
6219             h = createSingle(h, el, ename, fn);
6220         }
6221         if(o.buffer){
6222             h = createBuffered(h, o);
6223         }
6224         fn._handlers = fn._handlers || [];
6225         
6226         
6227         fn._handlers.push([Roo.id(el), ename, h]);
6228         
6229         
6230          
6231         E.on(el, ename, h);
6232         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6233             el.addEventListener("DOMMouseScroll", h, false);
6234             E.on(window, 'unload', function(){
6235                 el.removeEventListener("DOMMouseScroll", h, false);
6236             });
6237         }
6238         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6239             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6240         }
6241         return h;
6242     };
6243
6244     var stopListening = function(el, ename, fn){
6245         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6246         if(hds){
6247             for(var i = 0, len = hds.length; i < len; i++){
6248                 var h = hds[i];
6249                 if(h[0] == id && h[1] == ename){
6250                     hd = h[2];
6251                     hds.splice(i, 1);
6252                     break;
6253                 }
6254             }
6255         }
6256         E.un(el, ename, hd);
6257         el = Roo.getDom(el);
6258         if(ename == "mousewheel" && el.addEventListener){
6259             el.removeEventListener("DOMMouseScroll", hd, false);
6260         }
6261         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6262             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6263         }
6264     };
6265
6266     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6267     
6268     var pub = {
6269         
6270         
6271         /** 
6272          * Fix for doc tools
6273          * @scope Roo.EventManager
6274          */
6275         
6276         
6277         /** 
6278          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6279          * object with a Roo.EventObject
6280          * @param {Function} fn        The method the event invokes
6281          * @param {Object}   scope    An object that becomes the scope of the handler
6282          * @param {boolean}  override If true, the obj passed in becomes
6283          *                             the execution scope of the listener
6284          * @return {Function} The wrapped function
6285          * @deprecated
6286          */
6287         wrap : function(fn, scope, override){
6288             return function(e){
6289                 Roo.EventObject.setEvent(e);
6290                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6291             };
6292         },
6293         
6294         /**
6295      * Appends an event handler to an element (shorthand for addListener)
6296      * @param {String/HTMLElement}   element        The html element or id to assign the
6297      * @param {String}   eventName The type of event to listen for
6298      * @param {Function} handler The method the event invokes
6299      * @param {Object}   scope (optional) The scope in which to execute the handler
6300      * function. The handler function's "this" context.
6301      * @param {Object}   options (optional) An object containing handler configuration
6302      * properties. This may contain any of the following properties:<ul>
6303      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6304      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6305      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6306      * <li>preventDefault {Boolean} True to prevent the default action</li>
6307      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6308      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6309      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6310      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6311      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6312      * by the specified number of milliseconds. If the event fires again within that time, the original
6313      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6314      * </ul><br>
6315      * <p>
6316      * <b>Combining Options</b><br>
6317      * Using the options argument, it is possible to combine different types of listeners:<br>
6318      * <br>
6319      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6320      * Code:<pre><code>
6321 el.on('click', this.onClick, this, {
6322     single: true,
6323     delay: 100,
6324     stopEvent : true,
6325     forumId: 4
6326 });</code></pre>
6327      * <p>
6328      * <b>Attaching multiple handlers in 1 call</b><br>
6329       * The method also allows for a single argument to be passed which is a config object containing properties
6330      * which specify multiple handlers.
6331      * <p>
6332      * Code:<pre><code>
6333 el.on({
6334     'click' : {
6335         fn: this.onClick
6336         scope: this,
6337         delay: 100
6338     },
6339     'mouseover' : {
6340         fn: this.onMouseOver
6341         scope: this
6342     },
6343     'mouseout' : {
6344         fn: this.onMouseOut
6345         scope: this
6346     }
6347 });</code></pre>
6348      * <p>
6349      * Or a shorthand syntax:<br>
6350      * Code:<pre><code>
6351 el.on({
6352     'click' : this.onClick,
6353     'mouseover' : this.onMouseOver,
6354     'mouseout' : this.onMouseOut
6355     scope: this
6356 });</code></pre>
6357      */
6358         addListener : function(element, eventName, fn, scope, options){
6359             if(typeof eventName == "object"){
6360                 var o = eventName;
6361                 for(var e in o){
6362                     if(propRe.test(e)){
6363                         continue;
6364                     }
6365                     if(typeof o[e] == "function"){
6366                         // shared options
6367                         listen(element, e, o, o[e], o.scope);
6368                     }else{
6369                         // individual options
6370                         listen(element, e, o[e]);
6371                     }
6372                 }
6373                 return;
6374             }
6375             return listen(element, eventName, options, fn, scope);
6376         },
6377         
6378         /**
6379          * Removes an event handler
6380          *
6381          * @param {String/HTMLElement}   element        The id or html element to remove the 
6382          *                             event from
6383          * @param {String}   eventName     The type of event
6384          * @param {Function} fn
6385          * @return {Boolean} True if a listener was actually removed
6386          */
6387         removeListener : function(element, eventName, fn){
6388             return stopListening(element, eventName, fn);
6389         },
6390         
6391         /**
6392          * Fires when the document is ready (before onload and before images are loaded). Can be 
6393          * accessed shorthanded Roo.onReady().
6394          * @param {Function} fn        The method the event invokes
6395          * @param {Object}   scope    An  object that becomes the scope of the handler
6396          * @param {boolean}  options
6397          */
6398         onDocumentReady : function(fn, scope, options){
6399             if(docReadyState){ // if it already fired
6400                 docReadyEvent.addListener(fn, scope, options);
6401                 docReadyEvent.fire();
6402                 docReadyEvent.clearListeners();
6403                 return;
6404             }
6405             if(!docReadyEvent){
6406                 initDocReady();
6407             }
6408             docReadyEvent.addListener(fn, scope, options);
6409         },
6410         
6411         /**
6412          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6413          * @param {Function} fn        The method the event invokes
6414          * @param {Object}   scope    An object that becomes the scope of the handler
6415          * @param {boolean}  options
6416          */
6417         onWindowResize : function(fn, scope, options){
6418             if(!resizeEvent){
6419                 resizeEvent = new Roo.util.Event();
6420                 resizeTask = new Roo.util.DelayedTask(function(){
6421                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6422                 });
6423                 E.on(window, "resize", function(){
6424                     if(Roo.isIE){
6425                         resizeTask.delay(50);
6426                     }else{
6427                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6428                     }
6429                 });
6430             }
6431             resizeEvent.addListener(fn, scope, options);
6432         },
6433
6434         /**
6435          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6436          * @param {Function} fn        The method the event invokes
6437          * @param {Object}   scope    An object that becomes the scope of the handler
6438          * @param {boolean}  options
6439          */
6440         onTextResize : function(fn, scope, options){
6441             if(!textEvent){
6442                 textEvent = new Roo.util.Event();
6443                 var textEl = new Roo.Element(document.createElement('div'));
6444                 textEl.dom.className = 'x-text-resize';
6445                 textEl.dom.innerHTML = 'X';
6446                 textEl.appendTo(document.body);
6447                 textSize = textEl.dom.offsetHeight;
6448                 setInterval(function(){
6449                     if(textEl.dom.offsetHeight != textSize){
6450                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6451                     }
6452                 }, this.textResizeInterval);
6453             }
6454             textEvent.addListener(fn, scope, options);
6455         },
6456
6457         /**
6458          * Removes the passed window resize listener.
6459          * @param {Function} fn        The method the event invokes
6460          * @param {Object}   scope    The scope of handler
6461          */
6462         removeResizeListener : function(fn, scope){
6463             if(resizeEvent){
6464                 resizeEvent.removeListener(fn, scope);
6465             }
6466         },
6467
6468         // private
6469         fireResize : function(){
6470             if(resizeEvent){
6471                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6472             }   
6473         },
6474         /**
6475          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6476          */
6477         ieDeferSrc : false,
6478         /**
6479          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6480          */
6481         textResizeInterval : 50
6482     };
6483     
6484     /**
6485      * Fix for doc tools
6486      * @scopeAlias pub=Roo.EventManager
6487      */
6488     
6489      /**
6490      * Appends an event handler to an element (shorthand for addListener)
6491      * @param {String/HTMLElement}   element        The html element or id to assign the
6492      * @param {String}   eventName The type of event to listen for
6493      * @param {Function} handler The method the event invokes
6494      * @param {Object}   scope (optional) The scope in which to execute the handler
6495      * function. The handler function's "this" context.
6496      * @param {Object}   options (optional) An object containing handler configuration
6497      * properties. This may contain any of the following properties:<ul>
6498      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6499      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6500      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6501      * <li>preventDefault {Boolean} True to prevent the default action</li>
6502      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6503      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6504      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6505      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6506      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6507      * by the specified number of milliseconds. If the event fires again within that time, the original
6508      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6509      * </ul><br>
6510      * <p>
6511      * <b>Combining Options</b><br>
6512      * Using the options argument, it is possible to combine different types of listeners:<br>
6513      * <br>
6514      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6515      * Code:<pre><code>
6516 el.on('click', this.onClick, this, {
6517     single: true,
6518     delay: 100,
6519     stopEvent : true,
6520     forumId: 4
6521 });</code></pre>
6522      * <p>
6523      * <b>Attaching multiple handlers in 1 call</b><br>
6524       * The method also allows for a single argument to be passed which is a config object containing properties
6525      * which specify multiple handlers.
6526      * <p>
6527      * Code:<pre><code>
6528 el.on({
6529     'click' : {
6530         fn: this.onClick
6531         scope: this,
6532         delay: 100
6533     },
6534     'mouseover' : {
6535         fn: this.onMouseOver
6536         scope: this
6537     },
6538     'mouseout' : {
6539         fn: this.onMouseOut
6540         scope: this
6541     }
6542 });</code></pre>
6543      * <p>
6544      * Or a shorthand syntax:<br>
6545      * Code:<pre><code>
6546 el.on({
6547     'click' : this.onClick,
6548     'mouseover' : this.onMouseOver,
6549     'mouseout' : this.onMouseOut
6550     scope: this
6551 });</code></pre>
6552      */
6553     pub.on = pub.addListener;
6554     pub.un = pub.removeListener;
6555
6556     pub.stoppedMouseDownEvent = new Roo.util.Event();
6557     return pub;
6558 }();
6559 /**
6560   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6561   * @param {Function} fn        The method the event invokes
6562   * @param {Object}   scope    An  object that becomes the scope of the handler
6563   * @param {boolean}  override If true, the obj passed in becomes
6564   *                             the execution scope of the listener
6565   * @member Roo
6566   * @method onReady
6567  */
6568 Roo.onReady = Roo.EventManager.onDocumentReady;
6569
6570 Roo.onReady(function(){
6571     var bd = Roo.get(document.body);
6572     if(!bd){ return; }
6573
6574     var cls = [
6575             Roo.isIE ? "roo-ie"
6576             : Roo.isGecko ? "roo-gecko"
6577             : Roo.isOpera ? "roo-opera"
6578             : Roo.isSafari ? "roo-safari" : ""];
6579
6580     if(Roo.isMac){
6581         cls.push("roo-mac");
6582     }
6583     if(Roo.isLinux){
6584         cls.push("roo-linux");
6585     }
6586     if(Roo.isIOS){
6587         cls.push("roo-ios");
6588     }
6589     if(Roo.isTouch){
6590         cls.push("roo-touch");
6591     }
6592     if(Roo.isBorderBox){
6593         cls.push('roo-border-box');
6594     }
6595     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6596         var p = bd.dom.parentNode;
6597         if(p){
6598             p.className += ' roo-strict';
6599         }
6600     }
6601     bd.addClass(cls.join(' '));
6602 });
6603
6604 /**
6605  * @class Roo.EventObject
6606  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6607  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6608  * Example:
6609  * <pre><code>
6610  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6611     e.preventDefault();
6612     var target = e.getTarget();
6613     ...
6614  }
6615  var myDiv = Roo.get("myDiv");
6616  myDiv.on("click", handleClick);
6617  //or
6618  Roo.EventManager.on("myDiv", 'click', handleClick);
6619  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6620  </code></pre>
6621  * @singleton
6622  */
6623 Roo.EventObject = function(){
6624     
6625     var E = Roo.lib.Event;
6626     
6627     // safari keypress events for special keys return bad keycodes
6628     var safariKeys = {
6629         63234 : 37, // left
6630         63235 : 39, // right
6631         63232 : 38, // up
6632         63233 : 40, // down
6633         63276 : 33, // page up
6634         63277 : 34, // page down
6635         63272 : 46, // delete
6636         63273 : 36, // home
6637         63275 : 35  // end
6638     };
6639
6640     // normalize button clicks
6641     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6642                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6643
6644     Roo.EventObjectImpl = function(e){
6645         if(e){
6646             this.setEvent(e.browserEvent || e);
6647         }
6648     };
6649     Roo.EventObjectImpl.prototype = {
6650         /**
6651          * Used to fix doc tools.
6652          * @scope Roo.EventObject.prototype
6653          */
6654             
6655
6656         
6657         
6658         /** The normal browser event */
6659         browserEvent : null,
6660         /** The button pressed in a mouse event */
6661         button : -1,
6662         /** True if the shift key was down during the event */
6663         shiftKey : false,
6664         /** True if the control key was down during the event */
6665         ctrlKey : false,
6666         /** True if the alt key was down during the event */
6667         altKey : false,
6668
6669         /** Key constant 
6670         * @type Number */
6671         BACKSPACE : 8,
6672         /** Key constant 
6673         * @type Number */
6674         TAB : 9,
6675         /** Key constant 
6676         * @type Number */
6677         RETURN : 13,
6678         /** Key constant 
6679         * @type Number */
6680         ENTER : 13,
6681         /** Key constant 
6682         * @type Number */
6683         SHIFT : 16,
6684         /** Key constant 
6685         * @type Number */
6686         CONTROL : 17,
6687         /** Key constant 
6688         * @type Number */
6689         ESC : 27,
6690         /** Key constant 
6691         * @type Number */
6692         SPACE : 32,
6693         /** Key constant 
6694         * @type Number */
6695         PAGEUP : 33,
6696         /** Key constant 
6697         * @type Number */
6698         PAGEDOWN : 34,
6699         /** Key constant 
6700         * @type Number */
6701         END : 35,
6702         /** Key constant 
6703         * @type Number */
6704         HOME : 36,
6705         /** Key constant 
6706         * @type Number */
6707         LEFT : 37,
6708         /** Key constant 
6709         * @type Number */
6710         UP : 38,
6711         /** Key constant 
6712         * @type Number */
6713         RIGHT : 39,
6714         /** Key constant 
6715         * @type Number */
6716         DOWN : 40,
6717         /** Key constant 
6718         * @type Number */
6719         DELETE : 46,
6720         /** Key constant 
6721         * @type Number */
6722         F5 : 116,
6723
6724            /** @private */
6725         setEvent : function(e){
6726             if(e == this || (e && e.browserEvent)){ // already wrapped
6727                 return e;
6728             }
6729             this.browserEvent = e;
6730             if(e){
6731                 // normalize buttons
6732                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6733                 if(e.type == 'click' && this.button == -1){
6734                     this.button = 0;
6735                 }
6736                 this.type = e.type;
6737                 this.shiftKey = e.shiftKey;
6738                 // mac metaKey behaves like ctrlKey
6739                 this.ctrlKey = e.ctrlKey || e.metaKey;
6740                 this.altKey = e.altKey;
6741                 // in getKey these will be normalized for the mac
6742                 this.keyCode = e.keyCode;
6743                 // keyup warnings on firefox.
6744                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6745                 // cache the target for the delayed and or buffered events
6746                 this.target = E.getTarget(e);
6747                 // same for XY
6748                 this.xy = E.getXY(e);
6749             }else{
6750                 this.button = -1;
6751                 this.shiftKey = false;
6752                 this.ctrlKey = false;
6753                 this.altKey = false;
6754                 this.keyCode = 0;
6755                 this.charCode =0;
6756                 this.target = null;
6757                 this.xy = [0, 0];
6758             }
6759             return this;
6760         },
6761
6762         /**
6763          * Stop the event (preventDefault and stopPropagation)
6764          */
6765         stopEvent : function(){
6766             if(this.browserEvent){
6767                 if(this.browserEvent.type == 'mousedown'){
6768                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6769                 }
6770                 E.stopEvent(this.browserEvent);
6771             }
6772         },
6773
6774         /**
6775          * Prevents the browsers default handling of the event.
6776          */
6777         preventDefault : function(){
6778             if(this.browserEvent){
6779                 E.preventDefault(this.browserEvent);
6780             }
6781         },
6782
6783         /** @private */
6784         isNavKeyPress : function(){
6785             var k = this.keyCode;
6786             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6787             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6788         },
6789
6790         isSpecialKey : function(){
6791             var k = this.keyCode;
6792             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6793             (k == 16) || (k == 17) ||
6794             (k >= 18 && k <= 20) ||
6795             (k >= 33 && k <= 35) ||
6796             (k >= 36 && k <= 39) ||
6797             (k >= 44 && k <= 45);
6798         },
6799         /**
6800          * Cancels bubbling of the event.
6801          */
6802         stopPropagation : function(){
6803             if(this.browserEvent){
6804                 if(this.type == 'mousedown'){
6805                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6806                 }
6807                 E.stopPropagation(this.browserEvent);
6808             }
6809         },
6810
6811         /**
6812          * Gets the key code for the event.
6813          * @return {Number}
6814          */
6815         getCharCode : function(){
6816             return this.charCode || this.keyCode;
6817         },
6818
6819         /**
6820          * Returns a normalized keyCode for the event.
6821          * @return {Number} The key code
6822          */
6823         getKey : function(){
6824             var k = this.keyCode || this.charCode;
6825             return Roo.isSafari ? (safariKeys[k] || k) : k;
6826         },
6827
6828         /**
6829          * Gets the x coordinate of the event.
6830          * @return {Number}
6831          */
6832         getPageX : function(){
6833             return this.xy[0];
6834         },
6835
6836         /**
6837          * Gets the y coordinate of the event.
6838          * @return {Number}
6839          */
6840         getPageY : function(){
6841             return this.xy[1];
6842         },
6843
6844         /**
6845          * Gets the time of the event.
6846          * @return {Number}
6847          */
6848         getTime : function(){
6849             if(this.browserEvent){
6850                 return E.getTime(this.browserEvent);
6851             }
6852             return null;
6853         },
6854
6855         /**
6856          * Gets the page coordinates of the event.
6857          * @return {Array} The xy values like [x, y]
6858          */
6859         getXY : function(){
6860             return this.xy;
6861         },
6862
6863         /**
6864          * Gets the target for the event.
6865          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6866          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6867                 search as a number or element (defaults to 10 || document.body)
6868          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6869          * @return {HTMLelement}
6870          */
6871         getTarget : function(selector, maxDepth, returnEl){
6872             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6873         },
6874         /**
6875          * Gets the related target.
6876          * @return {HTMLElement}
6877          */
6878         getRelatedTarget : function(){
6879             if(this.browserEvent){
6880                 return E.getRelatedTarget(this.browserEvent);
6881             }
6882             return null;
6883         },
6884
6885         /**
6886          * Normalizes mouse wheel delta across browsers
6887          * @return {Number} The delta
6888          */
6889         getWheelDelta : function(){
6890             var e = this.browserEvent;
6891             var delta = 0;
6892             if(e.wheelDelta){ /* IE/Opera. */
6893                 delta = e.wheelDelta/120;
6894             }else if(e.detail){ /* Mozilla case. */
6895                 delta = -e.detail/3;
6896             }
6897             return delta;
6898         },
6899
6900         /**
6901          * Returns true if the control, meta, shift or alt key was pressed during this event.
6902          * @return {Boolean}
6903          */
6904         hasModifier : function(){
6905             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6906         },
6907
6908         /**
6909          * Returns true if the target of this event equals el or is a child of el
6910          * @param {String/HTMLElement/Element} el
6911          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6912          * @return {Boolean}
6913          */
6914         within : function(el, related){
6915             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6916             return t && Roo.fly(el).contains(t);
6917         },
6918
6919         getPoint : function(){
6920             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6921         }
6922     };
6923
6924     return new Roo.EventObjectImpl();
6925 }();
6926             
6927     /*
6928  * Based on:
6929  * Ext JS Library 1.1.1
6930  * Copyright(c) 2006-2007, Ext JS, LLC.
6931  *
6932  * Originally Released Under LGPL - original licence link has changed is not relivant.
6933  *
6934  * Fork - LGPL
6935  * <script type="text/javascript">
6936  */
6937
6938  
6939 // was in Composite Element!??!?!
6940  
6941 (function(){
6942     var D = Roo.lib.Dom;
6943     var E = Roo.lib.Event;
6944     var A = Roo.lib.Anim;
6945
6946     // local style camelizing for speed
6947     var propCache = {};
6948     var camelRe = /(-[a-z])/gi;
6949     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6950     var view = document.defaultView;
6951
6952 /**
6953  * @class Roo.Element
6954  * Represents an Element in the DOM.<br><br>
6955  * Usage:<br>
6956 <pre><code>
6957 var el = Roo.get("my-div");
6958
6959 // or with getEl
6960 var el = getEl("my-div");
6961
6962 // or with a DOM element
6963 var el = Roo.get(myDivElement);
6964 </code></pre>
6965  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6966  * each call instead of constructing a new one.<br><br>
6967  * <b>Animations</b><br />
6968  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6969  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6970 <pre>
6971 Option    Default   Description
6972 --------- --------  ---------------------------------------------
6973 duration  .35       The duration of the animation in seconds
6974 easing    easeOut   The YUI easing method
6975 callback  none      A function to execute when the anim completes
6976 scope     this      The scope (this) of the callback function
6977 </pre>
6978 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6979 * manipulate the animation. Here's an example:
6980 <pre><code>
6981 var el = Roo.get("my-div");
6982
6983 // no animation
6984 el.setWidth(100);
6985
6986 // default animation
6987 el.setWidth(100, true);
6988
6989 // animation with some options set
6990 el.setWidth(100, {
6991     duration: 1,
6992     callback: this.foo,
6993     scope: this
6994 });
6995
6996 // using the "anim" property to get the Anim object
6997 var opt = {
6998     duration: 1,
6999     callback: this.foo,
7000     scope: this
7001 };
7002 el.setWidth(100, opt);
7003 ...
7004 if(opt.anim.isAnimated()){
7005     opt.anim.stop();
7006 }
7007 </code></pre>
7008 * <b> Composite (Collections of) Elements</b><br />
7009  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7010  * @constructor Create a new Element directly.
7011  * @param {String/HTMLElement} element
7012  * @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).
7013  */
7014     Roo.Element = function(element, forceNew){
7015         var dom = typeof element == "string" ?
7016                 document.getElementById(element) : element;
7017         if(!dom){ // invalid id/element
7018             return null;
7019         }
7020         var id = dom.id;
7021         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7022             return Roo.Element.cache[id];
7023         }
7024
7025         /**
7026          * The DOM element
7027          * @type HTMLElement
7028          */
7029         this.dom = dom;
7030
7031         /**
7032          * The DOM element ID
7033          * @type String
7034          */
7035         this.id = id || Roo.id(dom);
7036     };
7037
7038     var El = Roo.Element;
7039
7040     El.prototype = {
7041         /**
7042          * The element's default display mode  (defaults to "")
7043          * @type String
7044          */
7045         originalDisplay : "",
7046
7047         visibilityMode : 1,
7048         /**
7049          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7050          * @type String
7051          */
7052         defaultUnit : "px",
7053         
7054         /**
7055          * Sets the element's visibility mode. When setVisible() is called it
7056          * will use this to determine whether to set the visibility or the display property.
7057          * @param visMode Element.VISIBILITY or Element.DISPLAY
7058          * @return {Roo.Element} this
7059          */
7060         setVisibilityMode : function(visMode){
7061             this.visibilityMode = visMode;
7062             return this;
7063         },
7064         /**
7065          * Convenience method for setVisibilityMode(Element.DISPLAY)
7066          * @param {String} display (optional) What to set display to when visible
7067          * @return {Roo.Element} this
7068          */
7069         enableDisplayMode : function(display){
7070             this.setVisibilityMode(El.DISPLAY);
7071             if(typeof display != "undefined") { this.originalDisplay = display; }
7072             return this;
7073         },
7074
7075         /**
7076          * 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)
7077          * @param {String} selector The simple selector to test
7078          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7079                 search as a number or element (defaults to 10 || document.body)
7080          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7081          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7082          */
7083         findParent : function(simpleSelector, maxDepth, returnEl){
7084             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7085             maxDepth = maxDepth || 50;
7086             if(typeof maxDepth != "number"){
7087                 stopEl = Roo.getDom(maxDepth);
7088                 maxDepth = 10;
7089             }
7090             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7091                 if(dq.is(p, simpleSelector)){
7092                     return returnEl ? Roo.get(p) : p;
7093                 }
7094                 depth++;
7095                 p = p.parentNode;
7096             }
7097             return null;
7098         },
7099
7100
7101         /**
7102          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7103          * @param {String} selector The simple selector to test
7104          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7105                 search as a number or element (defaults to 10 || document.body)
7106          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7107          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7108          */
7109         findParentNode : function(simpleSelector, maxDepth, returnEl){
7110             var p = Roo.fly(this.dom.parentNode, '_internal');
7111             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7112         },
7113
7114         /**
7115          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7116          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7117          * @param {String} selector The simple selector to test
7118          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7119                 search as a number or element (defaults to 10 || document.body)
7120          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7121          */
7122         up : function(simpleSelector, maxDepth){
7123             return this.findParentNode(simpleSelector, maxDepth, true);
7124         },
7125
7126
7127
7128         /**
7129          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7130          * @param {String} selector The simple selector to test
7131          * @return {Boolean} True if this element matches the selector, else false
7132          */
7133         is : function(simpleSelector){
7134             return Roo.DomQuery.is(this.dom, simpleSelector);
7135         },
7136
7137         /**
7138          * Perform animation on this element.
7139          * @param {Object} args The YUI animation control args
7140          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7141          * @param {Function} onComplete (optional) Function to call when animation completes
7142          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7143          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7144          * @return {Roo.Element} this
7145          */
7146         animate : function(args, duration, onComplete, easing, animType){
7147             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7148             return this;
7149         },
7150
7151         /*
7152          * @private Internal animation call
7153          */
7154         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7155             animType = animType || 'run';
7156             opt = opt || {};
7157             var anim = Roo.lib.Anim[animType](
7158                 this.dom, args,
7159                 (opt.duration || defaultDur) || .35,
7160                 (opt.easing || defaultEase) || 'easeOut',
7161                 function(){
7162                     Roo.callback(cb, this);
7163                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7164                 },
7165                 this
7166             );
7167             opt.anim = anim;
7168             return anim;
7169         },
7170
7171         // private legacy anim prep
7172         preanim : function(a, i){
7173             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7174         },
7175
7176         /**
7177          * Removes worthless text nodes
7178          * @param {Boolean} forceReclean (optional) By default the element
7179          * keeps track if it has been cleaned already so
7180          * you can call this over and over. However, if you update the element and
7181          * need to force a reclean, you can pass true.
7182          */
7183         clean : function(forceReclean){
7184             if(this.isCleaned && forceReclean !== true){
7185                 return this;
7186             }
7187             var ns = /\S/;
7188             var d = this.dom, n = d.firstChild, ni = -1;
7189             while(n){
7190                 var nx = n.nextSibling;
7191                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7192                     d.removeChild(n);
7193                 }else{
7194                     n.nodeIndex = ++ni;
7195                 }
7196                 n = nx;
7197             }
7198             this.isCleaned = true;
7199             return this;
7200         },
7201
7202         // private
7203         calcOffsetsTo : function(el){
7204             el = Roo.get(el);
7205             var d = el.dom;
7206             var restorePos = false;
7207             if(el.getStyle('position') == 'static'){
7208                 el.position('relative');
7209                 restorePos = true;
7210             }
7211             var x = 0, y =0;
7212             var op = this.dom;
7213             while(op && op != d && op.tagName != 'HTML'){
7214                 x+= op.offsetLeft;
7215                 y+= op.offsetTop;
7216                 op = op.offsetParent;
7217             }
7218             if(restorePos){
7219                 el.position('static');
7220             }
7221             return [x, y];
7222         },
7223
7224         /**
7225          * Scrolls this element into view within the passed container.
7226          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7227          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7228          * @return {Roo.Element} this
7229          */
7230         scrollIntoView : function(container, hscroll){
7231             var c = Roo.getDom(container) || document.body;
7232             var el = this.dom;
7233
7234             var o = this.calcOffsetsTo(c),
7235                 l = o[0],
7236                 t = o[1],
7237                 b = t+el.offsetHeight,
7238                 r = l+el.offsetWidth;
7239
7240             var ch = c.clientHeight;
7241             var ct = parseInt(c.scrollTop, 10);
7242             var cl = parseInt(c.scrollLeft, 10);
7243             var cb = ct + ch;
7244             var cr = cl + c.clientWidth;
7245
7246             if(t < ct){
7247                 c.scrollTop = t;
7248             }else if(b > cb){
7249                 c.scrollTop = b-ch;
7250             }
7251
7252             if(hscroll !== false){
7253                 if(l < cl){
7254                     c.scrollLeft = l;
7255                 }else if(r > cr){
7256                     c.scrollLeft = r-c.clientWidth;
7257                 }
7258             }
7259             return this;
7260         },
7261
7262         // private
7263         scrollChildIntoView : function(child, hscroll){
7264             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7265         },
7266
7267         /**
7268          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7269          * the new height may not be available immediately.
7270          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7271          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7272          * @param {Function} onComplete (optional) Function to call when animation completes
7273          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7274          * @return {Roo.Element} this
7275          */
7276         autoHeight : function(animate, duration, onComplete, easing){
7277             var oldHeight = this.getHeight();
7278             this.clip();
7279             this.setHeight(1); // force clipping
7280             setTimeout(function(){
7281                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7282                 if(!animate){
7283                     this.setHeight(height);
7284                     this.unclip();
7285                     if(typeof onComplete == "function"){
7286                         onComplete();
7287                     }
7288                 }else{
7289                     this.setHeight(oldHeight); // restore original height
7290                     this.setHeight(height, animate, duration, function(){
7291                         this.unclip();
7292                         if(typeof onComplete == "function") { onComplete(); }
7293                     }.createDelegate(this), easing);
7294                 }
7295             }.createDelegate(this), 0);
7296             return this;
7297         },
7298
7299         /**
7300          * Returns true if this element is an ancestor of the passed element
7301          * @param {HTMLElement/String} el The element to check
7302          * @return {Boolean} True if this element is an ancestor of el, else false
7303          */
7304         contains : function(el){
7305             if(!el){return false;}
7306             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7307         },
7308
7309         /**
7310          * Checks whether the element is currently visible using both visibility and display properties.
7311          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7312          * @return {Boolean} True if the element is currently visible, else false
7313          */
7314         isVisible : function(deep) {
7315             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7316             if(deep !== true || !vis){
7317                 return vis;
7318             }
7319             var p = this.dom.parentNode;
7320             while(p && p.tagName.toLowerCase() != "body"){
7321                 if(!Roo.fly(p, '_isVisible').isVisible()){
7322                     return false;
7323                 }
7324                 p = p.parentNode;
7325             }
7326             return true;
7327         },
7328
7329         /**
7330          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7331          * @param {String} selector The CSS selector
7332          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7333          * @return {CompositeElement/CompositeElementLite} The composite element
7334          */
7335         select : function(selector, unique){
7336             return El.select(selector, unique, this.dom);
7337         },
7338
7339         /**
7340          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7341          * @param {String} selector The CSS selector
7342          * @return {Array} An array of the matched nodes
7343          */
7344         query : function(selector, unique){
7345             return Roo.DomQuery.select(selector, this.dom);
7346         },
7347
7348         /**
7349          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7350          * @param {String} selector The CSS selector
7351          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7352          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7353          */
7354         child : function(selector, returnDom){
7355             var n = Roo.DomQuery.selectNode(selector, this.dom);
7356             return returnDom ? n : Roo.get(n);
7357         },
7358
7359         /**
7360          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7361          * @param {String} selector The CSS selector
7362          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7363          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7364          */
7365         down : function(selector, returnDom){
7366             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7367             return returnDom ? n : Roo.get(n);
7368         },
7369
7370         /**
7371          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7372          * @param {String} group The group the DD object is member of
7373          * @param {Object} config The DD config object
7374          * @param {Object} overrides An object containing methods to override/implement on the DD object
7375          * @return {Roo.dd.DD} The DD object
7376          */
7377         initDD : function(group, config, overrides){
7378             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7379             return Roo.apply(dd, overrides);
7380         },
7381
7382         /**
7383          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7384          * @param {String} group The group the DDProxy object is member of
7385          * @param {Object} config The DDProxy config object
7386          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7387          * @return {Roo.dd.DDProxy} The DDProxy object
7388          */
7389         initDDProxy : function(group, config, overrides){
7390             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7391             return Roo.apply(dd, overrides);
7392         },
7393
7394         /**
7395          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7396          * @param {String} group The group the DDTarget object is member of
7397          * @param {Object} config The DDTarget config object
7398          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7399          * @return {Roo.dd.DDTarget} The DDTarget object
7400          */
7401         initDDTarget : function(group, config, overrides){
7402             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7403             return Roo.apply(dd, overrides);
7404         },
7405
7406         /**
7407          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7408          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7409          * @param {Boolean} visible Whether the element is visible
7410          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7411          * @return {Roo.Element} this
7412          */
7413          setVisible : function(visible, animate){
7414             if(!animate || !A){
7415                 if(this.visibilityMode == El.DISPLAY){
7416                     this.setDisplayed(visible);
7417                 }else{
7418                     this.fixDisplay();
7419                     this.dom.style.visibility = visible ? "visible" : "hidden";
7420                 }
7421             }else{
7422                 // closure for composites
7423                 var dom = this.dom;
7424                 var visMode = this.visibilityMode;
7425                 if(visible){
7426                     this.setOpacity(.01);
7427                     this.setVisible(true);
7428                 }
7429                 this.anim({opacity: { to: (visible?1:0) }},
7430                       this.preanim(arguments, 1),
7431                       null, .35, 'easeIn', function(){
7432                          if(!visible){
7433                              if(visMode == El.DISPLAY){
7434                                  dom.style.display = "none";
7435                              }else{
7436                                  dom.style.visibility = "hidden";
7437                              }
7438                              Roo.get(dom).setOpacity(1);
7439                          }
7440                      });
7441             }
7442             return this;
7443         },
7444
7445         /**
7446          * Returns true if display is not "none"
7447          * @return {Boolean}
7448          */
7449         isDisplayed : function() {
7450             return this.getStyle("display") != "none";
7451         },
7452
7453         /**
7454          * Toggles the element's visibility or display, depending on visibility mode.
7455          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7456          * @return {Roo.Element} this
7457          */
7458         toggle : function(animate){
7459             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7460             return this;
7461         },
7462
7463         /**
7464          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7465          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7466          * @return {Roo.Element} this
7467          */
7468         setDisplayed : function(value) {
7469             if(typeof value == "boolean"){
7470                value = value ? this.originalDisplay : "none";
7471             }
7472             this.setStyle("display", value);
7473             return this;
7474         },
7475
7476         /**
7477          * Tries to focus the element. Any exceptions are caught and ignored.
7478          * @return {Roo.Element} this
7479          */
7480         focus : function() {
7481             try{
7482                 this.dom.focus();
7483             }catch(e){}
7484             return this;
7485         },
7486
7487         /**
7488          * Tries to blur the element. Any exceptions are caught and ignored.
7489          * @return {Roo.Element} this
7490          */
7491         blur : function() {
7492             try{
7493                 this.dom.blur();
7494             }catch(e){}
7495             return this;
7496         },
7497
7498         /**
7499          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7500          * @param {String/Array} className The CSS class to add, or an array of classes
7501          * @return {Roo.Element} this
7502          */
7503         addClass : function(className){
7504             if(className instanceof Array){
7505                 for(var i = 0, len = className.length; i < len; i++) {
7506                     this.addClass(className[i]);
7507                 }
7508             }else{
7509                 if(className && !this.hasClass(className)){
7510                     this.dom.className = this.dom.className + " " + className;
7511                 }
7512             }
7513             return this;
7514         },
7515
7516         /**
7517          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7518          * @param {String/Array} className The CSS class to add, or an array of classes
7519          * @return {Roo.Element} this
7520          */
7521         radioClass : function(className){
7522             var siblings = this.dom.parentNode.childNodes;
7523             for(var i = 0; i < siblings.length; i++) {
7524                 var s = siblings[i];
7525                 if(s.nodeType == 1){
7526                     Roo.get(s).removeClass(className);
7527                 }
7528             }
7529             this.addClass(className);
7530             return this;
7531         },
7532
7533         /**
7534          * Removes one or more CSS classes from the element.
7535          * @param {String/Array} className The CSS class to remove, or an array of classes
7536          * @return {Roo.Element} this
7537          */
7538         removeClass : function(className){
7539             if(!className || !this.dom.className){
7540                 return this;
7541             }
7542             if(className instanceof Array){
7543                 for(var i = 0, len = className.length; i < len; i++) {
7544                     this.removeClass(className[i]);
7545                 }
7546             }else{
7547                 if(this.hasClass(className)){
7548                     var re = this.classReCache[className];
7549                     if (!re) {
7550                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7551                        this.classReCache[className] = re;
7552                     }
7553                     this.dom.className =
7554                         this.dom.className.replace(re, " ");
7555                 }
7556             }
7557             return this;
7558         },
7559
7560         // private
7561         classReCache: {},
7562
7563         /**
7564          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7565          * @param {String} className The CSS class to toggle
7566          * @return {Roo.Element} this
7567          */
7568         toggleClass : function(className){
7569             if(this.hasClass(className)){
7570                 this.removeClass(className);
7571             }else{
7572                 this.addClass(className);
7573             }
7574             return this;
7575         },
7576
7577         /**
7578          * Checks if the specified CSS class exists on this element's DOM node.
7579          * @param {String} className The CSS class to check for
7580          * @return {Boolean} True if the class exists, else false
7581          */
7582         hasClass : function(className){
7583             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7584         },
7585
7586         /**
7587          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7588          * @param {String} oldClassName The CSS class to replace
7589          * @param {String} newClassName The replacement CSS class
7590          * @return {Roo.Element} this
7591          */
7592         replaceClass : function(oldClassName, newClassName){
7593             this.removeClass(oldClassName);
7594             this.addClass(newClassName);
7595             return this;
7596         },
7597
7598         /**
7599          * Returns an object with properties matching the styles requested.
7600          * For example, el.getStyles('color', 'font-size', 'width') might return
7601          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7602          * @param {String} style1 A style name
7603          * @param {String} style2 A style name
7604          * @param {String} etc.
7605          * @return {Object} The style object
7606          */
7607         getStyles : function(){
7608             var a = arguments, len = a.length, r = {};
7609             for(var i = 0; i < len; i++){
7610                 r[a[i]] = this.getStyle(a[i]);
7611             }
7612             return r;
7613         },
7614
7615         /**
7616          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7617          * @param {String} property The style property whose value is returned.
7618          * @return {String} The current value of the style property for this element.
7619          */
7620         getStyle : function(){
7621             return view && view.getComputedStyle ?
7622                 function(prop){
7623                     var el = this.dom, v, cs, camel;
7624                     if(prop == 'float'){
7625                         prop = "cssFloat";
7626                     }
7627                     if(el.style && (v = el.style[prop])){
7628                         return v;
7629                     }
7630                     if(cs = view.getComputedStyle(el, "")){
7631                         if(!(camel = propCache[prop])){
7632                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7633                         }
7634                         return cs[camel];
7635                     }
7636                     return null;
7637                 } :
7638                 function(prop){
7639                     var el = this.dom, v, cs, camel;
7640                     if(prop == 'opacity'){
7641                         if(typeof el.style.filter == 'string'){
7642                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7643                             if(m){
7644                                 var fv = parseFloat(m[1]);
7645                                 if(!isNaN(fv)){
7646                                     return fv ? fv / 100 : 0;
7647                                 }
7648                             }
7649                         }
7650                         return 1;
7651                     }else if(prop == 'float'){
7652                         prop = "styleFloat";
7653                     }
7654                     if(!(camel = propCache[prop])){
7655                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7656                     }
7657                     if(v = el.style[camel]){
7658                         return v;
7659                     }
7660                     if(cs = el.currentStyle){
7661                         return cs[camel];
7662                     }
7663                     return null;
7664                 };
7665         }(),
7666
7667         /**
7668          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7669          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7670          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7671          * @return {Roo.Element} this
7672          */
7673         setStyle : function(prop, value){
7674             if(typeof prop == "string"){
7675                 
7676                 if (prop == 'float') {
7677                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7678                     return this;
7679                 }
7680                 
7681                 var camel;
7682                 if(!(camel = propCache[prop])){
7683                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7684                 }
7685                 
7686                 if(camel == 'opacity') {
7687                     this.setOpacity(value);
7688                 }else{
7689                     this.dom.style[camel] = value;
7690                 }
7691             }else{
7692                 for(var style in prop){
7693                     if(typeof prop[style] != "function"){
7694                        this.setStyle(style, prop[style]);
7695                     }
7696                 }
7697             }
7698             return this;
7699         },
7700
7701         /**
7702          * More flexible version of {@link #setStyle} for setting style properties.
7703          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7704          * a function which returns such a specification.
7705          * @return {Roo.Element} this
7706          */
7707         applyStyles : function(style){
7708             Roo.DomHelper.applyStyles(this.dom, style);
7709             return this;
7710         },
7711
7712         /**
7713           * 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).
7714           * @return {Number} The X position of the element
7715           */
7716         getX : function(){
7717             return D.getX(this.dom);
7718         },
7719
7720         /**
7721           * 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).
7722           * @return {Number} The Y position of the element
7723           */
7724         getY : function(){
7725             return D.getY(this.dom);
7726         },
7727
7728         /**
7729           * 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).
7730           * @return {Array} The XY position of the element
7731           */
7732         getXY : function(){
7733             return D.getXY(this.dom);
7734         },
7735
7736         /**
7737          * 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).
7738          * @param {Number} The X position of the element
7739          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7740          * @return {Roo.Element} this
7741          */
7742         setX : function(x, animate){
7743             if(!animate || !A){
7744                 D.setX(this.dom, x);
7745             }else{
7746                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7747             }
7748             return this;
7749         },
7750
7751         /**
7752          * 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).
7753          * @param {Number} The Y position of the element
7754          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7755          * @return {Roo.Element} this
7756          */
7757         setY : function(y, animate){
7758             if(!animate || !A){
7759                 D.setY(this.dom, y);
7760             }else{
7761                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7762             }
7763             return this;
7764         },
7765
7766         /**
7767          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7768          * @param {String} left The left CSS property value
7769          * @return {Roo.Element} this
7770          */
7771         setLeft : function(left){
7772             this.setStyle("left", this.addUnits(left));
7773             return this;
7774         },
7775
7776         /**
7777          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7778          * @param {String} top The top CSS property value
7779          * @return {Roo.Element} this
7780          */
7781         setTop : function(top){
7782             this.setStyle("top", this.addUnits(top));
7783             return this;
7784         },
7785
7786         /**
7787          * Sets the element's CSS right style.
7788          * @param {String} right The right CSS property value
7789          * @return {Roo.Element} this
7790          */
7791         setRight : function(right){
7792             this.setStyle("right", this.addUnits(right));
7793             return this;
7794         },
7795
7796         /**
7797          * Sets the element's CSS bottom style.
7798          * @param {String} bottom The bottom CSS property value
7799          * @return {Roo.Element} this
7800          */
7801         setBottom : function(bottom){
7802             this.setStyle("bottom", this.addUnits(bottom));
7803             return this;
7804         },
7805
7806         /**
7807          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7808          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7809          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7810          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7811          * @return {Roo.Element} this
7812          */
7813         setXY : function(pos, animate){
7814             if(!animate || !A){
7815                 D.setXY(this.dom, pos);
7816             }else{
7817                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7818             }
7819             return this;
7820         },
7821
7822         /**
7823          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7824          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7825          * @param {Number} x X value for new position (coordinates are page-based)
7826          * @param {Number} y Y value for new position (coordinates are page-based)
7827          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7828          * @return {Roo.Element} this
7829          */
7830         setLocation : function(x, y, animate){
7831             this.setXY([x, y], this.preanim(arguments, 2));
7832             return this;
7833         },
7834
7835         /**
7836          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7837          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7838          * @param {Number} x X value for new position (coordinates are page-based)
7839          * @param {Number} y Y value for new position (coordinates are page-based)
7840          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7841          * @return {Roo.Element} this
7842          */
7843         moveTo : function(x, y, animate){
7844             this.setXY([x, y], this.preanim(arguments, 2));
7845             return this;
7846         },
7847
7848         /**
7849          * Returns the region of the given element.
7850          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7851          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7852          */
7853         getRegion : function(){
7854             return D.getRegion(this.dom);
7855         },
7856
7857         /**
7858          * Returns the offset height of the element
7859          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7860          * @return {Number} The element's height
7861          */
7862         getHeight : function(contentHeight){
7863             var h = this.dom.offsetHeight || 0;
7864             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7865         },
7866
7867         /**
7868          * Returns the offset width of the element
7869          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7870          * @return {Number} The element's width
7871          */
7872         getWidth : function(contentWidth){
7873             var w = this.dom.offsetWidth || 0;
7874             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7875         },
7876
7877         /**
7878          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7879          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7880          * if a height has not been set using CSS.
7881          * @return {Number}
7882          */
7883         getComputedHeight : function(){
7884             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7885             if(!h){
7886                 h = parseInt(this.getStyle('height'), 10) || 0;
7887                 if(!this.isBorderBox()){
7888                     h += this.getFrameWidth('tb');
7889                 }
7890             }
7891             return h;
7892         },
7893
7894         /**
7895          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7896          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7897          * if a width has not been set using CSS.
7898          * @return {Number}
7899          */
7900         getComputedWidth : function(){
7901             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7902             if(!w){
7903                 w = parseInt(this.getStyle('width'), 10) || 0;
7904                 if(!this.isBorderBox()){
7905                     w += this.getFrameWidth('lr');
7906                 }
7907             }
7908             return w;
7909         },
7910
7911         /**
7912          * Returns the size of the element.
7913          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7914          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7915          */
7916         getSize : function(contentSize){
7917             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7918         },
7919
7920         /**
7921          * Returns the width and height of the viewport.
7922          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7923          */
7924         getViewSize : function(){
7925             var d = this.dom, doc = document, aw = 0, ah = 0;
7926             if(d == doc || d == doc.body){
7927                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7928             }else{
7929                 return {
7930                     width : d.clientWidth,
7931                     height: d.clientHeight
7932                 };
7933             }
7934         },
7935
7936         /**
7937          * Returns the value of the "value" attribute
7938          * @param {Boolean} asNumber true to parse the value as a number
7939          * @return {String/Number}
7940          */
7941         getValue : function(asNumber){
7942             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7943         },
7944
7945         // private
7946         adjustWidth : function(width){
7947             if(typeof width == "number"){
7948                 if(this.autoBoxAdjust && !this.isBorderBox()){
7949                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7950                 }
7951                 if(width < 0){
7952                     width = 0;
7953                 }
7954             }
7955             return width;
7956         },
7957
7958         // private
7959         adjustHeight : function(height){
7960             if(typeof height == "number"){
7961                if(this.autoBoxAdjust && !this.isBorderBox()){
7962                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7963                }
7964                if(height < 0){
7965                    height = 0;
7966                }
7967             }
7968             return height;
7969         },
7970
7971         /**
7972          * Set the width of the element
7973          * @param {Number} width The new width
7974          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7975          * @return {Roo.Element} this
7976          */
7977         setWidth : function(width, animate){
7978             width = this.adjustWidth(width);
7979             if(!animate || !A){
7980                 this.dom.style.width = this.addUnits(width);
7981             }else{
7982                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7983             }
7984             return this;
7985         },
7986
7987         /**
7988          * Set the height of the element
7989          * @param {Number} height The new height
7990          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7991          * @return {Roo.Element} this
7992          */
7993          setHeight : function(height, animate){
7994             height = this.adjustHeight(height);
7995             if(!animate || !A){
7996                 this.dom.style.height = this.addUnits(height);
7997             }else{
7998                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7999             }
8000             return this;
8001         },
8002
8003         /**
8004          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8005          * @param {Number} width The new width
8006          * @param {Number} height The new height
8007          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8008          * @return {Roo.Element} this
8009          */
8010          setSize : function(width, height, animate){
8011             if(typeof width == "object"){ // in case of object from getSize()
8012                 height = width.height; width = width.width;
8013             }
8014             width = this.adjustWidth(width); height = this.adjustHeight(height);
8015             if(!animate || !A){
8016                 this.dom.style.width = this.addUnits(width);
8017                 this.dom.style.height = this.addUnits(height);
8018             }else{
8019                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8020             }
8021             return this;
8022         },
8023
8024         /**
8025          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8026          * @param {Number} x X value for new position (coordinates are page-based)
8027          * @param {Number} y Y value for new position (coordinates are page-based)
8028          * @param {Number} width The new width
8029          * @param {Number} height The new height
8030          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8031          * @return {Roo.Element} this
8032          */
8033         setBounds : function(x, y, width, height, animate){
8034             if(!animate || !A){
8035                 this.setSize(width, height);
8036                 this.setLocation(x, y);
8037             }else{
8038                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8039                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8040                               this.preanim(arguments, 4), 'motion');
8041             }
8042             return this;
8043         },
8044
8045         /**
8046          * 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.
8047          * @param {Roo.lib.Region} region The region to fill
8048          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8049          * @return {Roo.Element} this
8050          */
8051         setRegion : function(region, animate){
8052             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8053             return this;
8054         },
8055
8056         /**
8057          * Appends an event handler
8058          *
8059          * @param {String}   eventName     The type of event to append
8060          * @param {Function} fn        The method the event invokes
8061          * @param {Object} scope       (optional) The scope (this object) of the fn
8062          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8063          */
8064         addListener : function(eventName, fn, scope, options){
8065             if (this.dom) {
8066                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8067             }
8068         },
8069
8070         /**
8071          * Removes an event handler from this element
8072          * @param {String} eventName the type of event to remove
8073          * @param {Function} fn the method the event invokes
8074          * @return {Roo.Element} this
8075          */
8076         removeListener : function(eventName, fn){
8077             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8078             return this;
8079         },
8080
8081         /**
8082          * Removes all previous added listeners from this element
8083          * @return {Roo.Element} this
8084          */
8085         removeAllListeners : function(){
8086             E.purgeElement(this.dom);
8087             return this;
8088         },
8089
8090         relayEvent : function(eventName, observable){
8091             this.on(eventName, function(e){
8092                 observable.fireEvent(eventName, e);
8093             });
8094         },
8095
8096         /**
8097          * Set the opacity of the element
8098          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8099          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8100          * @return {Roo.Element} this
8101          */
8102          setOpacity : function(opacity, animate){
8103             if(!animate || !A){
8104                 var s = this.dom.style;
8105                 if(Roo.isIE){
8106                     s.zoom = 1;
8107                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8108                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8109                 }else{
8110                     s.opacity = opacity;
8111                 }
8112             }else{
8113                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8114             }
8115             return this;
8116         },
8117
8118         /**
8119          * Gets the left X coordinate
8120          * @param {Boolean} local True to get the local css position instead of page coordinate
8121          * @return {Number}
8122          */
8123         getLeft : function(local){
8124             if(!local){
8125                 return this.getX();
8126             }else{
8127                 return parseInt(this.getStyle("left"), 10) || 0;
8128             }
8129         },
8130
8131         /**
8132          * Gets the right X coordinate of the element (element X position + element width)
8133          * @param {Boolean} local True to get the local css position instead of page coordinate
8134          * @return {Number}
8135          */
8136         getRight : function(local){
8137             if(!local){
8138                 return this.getX() + this.getWidth();
8139             }else{
8140                 return (this.getLeft(true) + this.getWidth()) || 0;
8141             }
8142         },
8143
8144         /**
8145          * Gets the top Y coordinate
8146          * @param {Boolean} local True to get the local css position instead of page coordinate
8147          * @return {Number}
8148          */
8149         getTop : function(local) {
8150             if(!local){
8151                 return this.getY();
8152             }else{
8153                 return parseInt(this.getStyle("top"), 10) || 0;
8154             }
8155         },
8156
8157         /**
8158          * Gets the bottom Y coordinate of the element (element Y position + element height)
8159          * @param {Boolean} local True to get the local css position instead of page coordinate
8160          * @return {Number}
8161          */
8162         getBottom : function(local){
8163             if(!local){
8164                 return this.getY() + this.getHeight();
8165             }else{
8166                 return (this.getTop(true) + this.getHeight()) || 0;
8167             }
8168         },
8169
8170         /**
8171         * Initializes positioning on this element. If a desired position is not passed, it will make the
8172         * the element positioned relative IF it is not already positioned.
8173         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8174         * @param {Number} zIndex (optional) The zIndex to apply
8175         * @param {Number} x (optional) Set the page X position
8176         * @param {Number} y (optional) Set the page Y position
8177         */
8178         position : function(pos, zIndex, x, y){
8179             if(!pos){
8180                if(this.getStyle('position') == 'static'){
8181                    this.setStyle('position', 'relative');
8182                }
8183             }else{
8184                 this.setStyle("position", pos);
8185             }
8186             if(zIndex){
8187                 this.setStyle("z-index", zIndex);
8188             }
8189             if(x !== undefined && y !== undefined){
8190                 this.setXY([x, y]);
8191             }else if(x !== undefined){
8192                 this.setX(x);
8193             }else if(y !== undefined){
8194                 this.setY(y);
8195             }
8196         },
8197
8198         /**
8199         * Clear positioning back to the default when the document was loaded
8200         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8201         * @return {Roo.Element} this
8202          */
8203         clearPositioning : function(value){
8204             value = value ||'';
8205             this.setStyle({
8206                 "left": value,
8207                 "right": value,
8208                 "top": value,
8209                 "bottom": value,
8210                 "z-index": "",
8211                 "position" : "static"
8212             });
8213             return this;
8214         },
8215
8216         /**
8217         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8218         * snapshot before performing an update and then restoring the element.
8219         * @return {Object}
8220         */
8221         getPositioning : function(){
8222             var l = this.getStyle("left");
8223             var t = this.getStyle("top");
8224             return {
8225                 "position" : this.getStyle("position"),
8226                 "left" : l,
8227                 "right" : l ? "" : this.getStyle("right"),
8228                 "top" : t,
8229                 "bottom" : t ? "" : this.getStyle("bottom"),
8230                 "z-index" : this.getStyle("z-index")
8231             };
8232         },
8233
8234         /**
8235          * Gets the width of the border(s) for the specified side(s)
8236          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8237          * passing lr would get the border (l)eft width + the border (r)ight width.
8238          * @return {Number} The width of the sides passed added together
8239          */
8240         getBorderWidth : function(side){
8241             return this.addStyles(side, El.borders);
8242         },
8243
8244         /**
8245          * Gets the width of the padding(s) for the specified side(s)
8246          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8247          * passing lr would get the padding (l)eft + the padding (r)ight.
8248          * @return {Number} The padding of the sides passed added together
8249          */
8250         getPadding : function(side){
8251             return this.addStyles(side, El.paddings);
8252         },
8253
8254         /**
8255         * Set positioning with an object returned by getPositioning().
8256         * @param {Object} posCfg
8257         * @return {Roo.Element} this
8258          */
8259         setPositioning : function(pc){
8260             this.applyStyles(pc);
8261             if(pc.right == "auto"){
8262                 this.dom.style.right = "";
8263             }
8264             if(pc.bottom == "auto"){
8265                 this.dom.style.bottom = "";
8266             }
8267             return this;
8268         },
8269
8270         // private
8271         fixDisplay : function(){
8272             if(this.getStyle("display") == "none"){
8273                 this.setStyle("visibility", "hidden");
8274                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8275                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8276                     this.setStyle("display", "block");
8277                 }
8278             }
8279         },
8280
8281         /**
8282          * Quick set left and top adding default units
8283          * @param {String} left The left CSS property value
8284          * @param {String} top The top CSS property value
8285          * @return {Roo.Element} this
8286          */
8287          setLeftTop : function(left, top){
8288             this.dom.style.left = this.addUnits(left);
8289             this.dom.style.top = this.addUnits(top);
8290             return this;
8291         },
8292
8293         /**
8294          * Move this element relative to its current position.
8295          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8296          * @param {Number} distance How far to move the element in pixels
8297          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8298          * @return {Roo.Element} this
8299          */
8300          move : function(direction, distance, animate){
8301             var xy = this.getXY();
8302             direction = direction.toLowerCase();
8303             switch(direction){
8304                 case "l":
8305                 case "left":
8306                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8307                     break;
8308                case "r":
8309                case "right":
8310                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8311                     break;
8312                case "t":
8313                case "top":
8314                case "up":
8315                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8316                     break;
8317                case "b":
8318                case "bottom":
8319                case "down":
8320                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8321                     break;
8322             }
8323             return this;
8324         },
8325
8326         /**
8327          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8328          * @return {Roo.Element} this
8329          */
8330         clip : function(){
8331             if(!this.isClipped){
8332                this.isClipped = true;
8333                this.originalClip = {
8334                    "o": this.getStyle("overflow"),
8335                    "x": this.getStyle("overflow-x"),
8336                    "y": this.getStyle("overflow-y")
8337                };
8338                this.setStyle("overflow", "hidden");
8339                this.setStyle("overflow-x", "hidden");
8340                this.setStyle("overflow-y", "hidden");
8341             }
8342             return this;
8343         },
8344
8345         /**
8346          *  Return clipping (overflow) to original clipping before clip() was called
8347          * @return {Roo.Element} this
8348          */
8349         unclip : function(){
8350             if(this.isClipped){
8351                 this.isClipped = false;
8352                 var o = this.originalClip;
8353                 if(o.o){this.setStyle("overflow", o.o);}
8354                 if(o.x){this.setStyle("overflow-x", o.x);}
8355                 if(o.y){this.setStyle("overflow-y", o.y);}
8356             }
8357             return this;
8358         },
8359
8360
8361         /**
8362          * Gets the x,y coordinates specified by the anchor position on the element.
8363          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8364          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8365          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8366          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8367          * @return {Array} [x, y] An array containing the element's x and y coordinates
8368          */
8369         getAnchorXY : function(anchor, local, s){
8370             //Passing a different size is useful for pre-calculating anchors,
8371             //especially for anchored animations that change the el size.
8372
8373             var w, h, vp = false;
8374             if(!s){
8375                 var d = this.dom;
8376                 if(d == document.body || d == document){
8377                     vp = true;
8378                     w = D.getViewWidth(); h = D.getViewHeight();
8379                 }else{
8380                     w = this.getWidth(); h = this.getHeight();
8381                 }
8382             }else{
8383                 w = s.width;  h = s.height;
8384             }
8385             var x = 0, y = 0, r = Math.round;
8386             switch((anchor || "tl").toLowerCase()){
8387                 case "c":
8388                     x = r(w*.5);
8389                     y = r(h*.5);
8390                 break;
8391                 case "t":
8392                     x = r(w*.5);
8393                     y = 0;
8394                 break;
8395                 case "l":
8396                     x = 0;
8397                     y = r(h*.5);
8398                 break;
8399                 case "r":
8400                     x = w;
8401                     y = r(h*.5);
8402                 break;
8403                 case "b":
8404                     x = r(w*.5);
8405                     y = h;
8406                 break;
8407                 case "tl":
8408                     x = 0;
8409                     y = 0;
8410                 break;
8411                 case "bl":
8412                     x = 0;
8413                     y = h;
8414                 break;
8415                 case "br":
8416                     x = w;
8417                     y = h;
8418                 break;
8419                 case "tr":
8420                     x = w;
8421                     y = 0;
8422                 break;
8423             }
8424             if(local === true){
8425                 return [x, y];
8426             }
8427             if(vp){
8428                 var sc = this.getScroll();
8429                 return [x + sc.left, y + sc.top];
8430             }
8431             //Add the element's offset xy
8432             var o = this.getXY();
8433             return [x+o[0], y+o[1]];
8434         },
8435
8436         /**
8437          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8438          * supported position values.
8439          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8440          * @param {String} position The position to align to.
8441          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8442          * @return {Array} [x, y]
8443          */
8444         getAlignToXY : function(el, p, o){
8445             el = Roo.get(el);
8446             var d = this.dom;
8447             if(!el.dom){
8448                 throw "Element.alignTo with an element that doesn't exist";
8449             }
8450             var c = false; //constrain to viewport
8451             var p1 = "", p2 = "";
8452             o = o || [0,0];
8453
8454             if(!p){
8455                 p = "tl-bl";
8456             }else if(p == "?"){
8457                 p = "tl-bl?";
8458             }else if(p.indexOf("-") == -1){
8459                 p = "tl-" + p;
8460             }
8461             p = p.toLowerCase();
8462             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8463             if(!m){
8464                throw "Element.alignTo with an invalid alignment " + p;
8465             }
8466             p1 = m[1]; p2 = m[2]; c = !!m[3];
8467
8468             //Subtract the aligned el's internal xy from the target's offset xy
8469             //plus custom offset to get the aligned el's new offset xy
8470             var a1 = this.getAnchorXY(p1, true);
8471             var a2 = el.getAnchorXY(p2, false);
8472             var x = a2[0] - a1[0] + o[0];
8473             var y = a2[1] - a1[1] + o[1];
8474             if(c){
8475                 //constrain the aligned el to viewport if necessary
8476                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8477                 // 5px of margin for ie
8478                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8479
8480                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8481                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8482                 //otherwise swap the aligned el to the opposite border of the target.
8483                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8484                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8485                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8486                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8487
8488                var doc = document;
8489                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8490                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8491
8492                if((x+w) > dw + scrollX){
8493                     x = swapX ? r.left-w : dw+scrollX-w;
8494                 }
8495                if(x < scrollX){
8496                    x = swapX ? r.right : scrollX;
8497                }
8498                if((y+h) > dh + scrollY){
8499                     y = swapY ? r.top-h : dh+scrollY-h;
8500                 }
8501                if (y < scrollY){
8502                    y = swapY ? r.bottom : scrollY;
8503                }
8504             }
8505             return [x,y];
8506         },
8507
8508         // private
8509         getConstrainToXY : function(){
8510             var os = {top:0, left:0, bottom:0, right: 0};
8511
8512             return function(el, local, offsets, proposedXY){
8513                 el = Roo.get(el);
8514                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8515
8516                 var vw, vh, vx = 0, vy = 0;
8517                 if(el.dom == document.body || el.dom == document){
8518                     vw = Roo.lib.Dom.getViewWidth();
8519                     vh = Roo.lib.Dom.getViewHeight();
8520                 }else{
8521                     vw = el.dom.clientWidth;
8522                     vh = el.dom.clientHeight;
8523                     if(!local){
8524                         var vxy = el.getXY();
8525                         vx = vxy[0];
8526                         vy = vxy[1];
8527                     }
8528                 }
8529
8530                 var s = el.getScroll();
8531
8532                 vx += offsets.left + s.left;
8533                 vy += offsets.top + s.top;
8534
8535                 vw -= offsets.right;
8536                 vh -= offsets.bottom;
8537
8538                 var vr = vx+vw;
8539                 var vb = vy+vh;
8540
8541                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8542                 var x = xy[0], y = xy[1];
8543                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8544
8545                 // only move it if it needs it
8546                 var moved = false;
8547
8548                 // first validate right/bottom
8549                 if((x + w) > vr){
8550                     x = vr - w;
8551                     moved = true;
8552                 }
8553                 if((y + h) > vb){
8554                     y = vb - h;
8555                     moved = true;
8556                 }
8557                 // then make sure top/left isn't negative
8558                 if(x < vx){
8559                     x = vx;
8560                     moved = true;
8561                 }
8562                 if(y < vy){
8563                     y = vy;
8564                     moved = true;
8565                 }
8566                 return moved ? [x, y] : false;
8567             };
8568         }(),
8569
8570         // private
8571         adjustForConstraints : function(xy, parent, offsets){
8572             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8573         },
8574
8575         /**
8576          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8577          * document it aligns it to the viewport.
8578          * The position parameter is optional, and can be specified in any one of the following formats:
8579          * <ul>
8580          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8581          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8582          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8583          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8584          *   <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
8585          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8586          * </ul>
8587          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8588          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8589          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8590          * that specified in order to enforce the viewport constraints.
8591          * Following are all of the supported anchor positions:
8592     <pre>
8593     Value  Description
8594     -----  -----------------------------
8595     tl     The top left corner (default)
8596     t      The center of the top edge
8597     tr     The top right corner
8598     l      The center of the left edge
8599     c      In the center of the element
8600     r      The center of the right edge
8601     bl     The bottom left corner
8602     b      The center of the bottom edge
8603     br     The bottom right corner
8604     </pre>
8605     Example Usage:
8606     <pre><code>
8607     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8608     el.alignTo("other-el");
8609
8610     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8611     el.alignTo("other-el", "tr?");
8612
8613     // align the bottom right corner of el with the center left edge of other-el
8614     el.alignTo("other-el", "br-l?");
8615
8616     // align the center of el with the bottom left corner of other-el and
8617     // adjust the x position by -6 pixels (and the y position by 0)
8618     el.alignTo("other-el", "c-bl", [-6, 0]);
8619     </code></pre>
8620          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8621          * @param {String} position The position to align to.
8622          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8623          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8624          * @return {Roo.Element} this
8625          */
8626         alignTo : function(element, position, offsets, animate){
8627             var xy = this.getAlignToXY(element, position, offsets);
8628             this.setXY(xy, this.preanim(arguments, 3));
8629             return this;
8630         },
8631
8632         /**
8633          * Anchors an element to another element and realigns it when the window is resized.
8634          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8635          * @param {String} position The position to align to.
8636          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8637          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8638          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8639          * is a number, it is used as the buffer delay (defaults to 50ms).
8640          * @param {Function} callback The function to call after the animation finishes
8641          * @return {Roo.Element} this
8642          */
8643         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8644             var action = function(){
8645                 this.alignTo(el, alignment, offsets, animate);
8646                 Roo.callback(callback, this);
8647             };
8648             Roo.EventManager.onWindowResize(action, this);
8649             var tm = typeof monitorScroll;
8650             if(tm != 'undefined'){
8651                 Roo.EventManager.on(window, 'scroll', action, this,
8652                     {buffer: tm == 'number' ? monitorScroll : 50});
8653             }
8654             action.call(this); // align immediately
8655             return this;
8656         },
8657         /**
8658          * Clears any opacity settings from this element. Required in some cases for IE.
8659          * @return {Roo.Element} this
8660          */
8661         clearOpacity : function(){
8662             if (window.ActiveXObject) {
8663                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8664                     this.dom.style.filter = "";
8665                 }
8666             } else {
8667                 this.dom.style.opacity = "";
8668                 this.dom.style["-moz-opacity"] = "";
8669                 this.dom.style["-khtml-opacity"] = "";
8670             }
8671             return this;
8672         },
8673
8674         /**
8675          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8676          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8677          * @return {Roo.Element} this
8678          */
8679         hide : function(animate){
8680             this.setVisible(false, this.preanim(arguments, 0));
8681             return this;
8682         },
8683
8684         /**
8685         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8686         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8687          * @return {Roo.Element} this
8688          */
8689         show : function(animate){
8690             this.setVisible(true, this.preanim(arguments, 0));
8691             return this;
8692         },
8693
8694         /**
8695          * @private Test if size has a unit, otherwise appends the default
8696          */
8697         addUnits : function(size){
8698             return Roo.Element.addUnits(size, this.defaultUnit);
8699         },
8700
8701         /**
8702          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8703          * @return {Roo.Element} this
8704          */
8705         beginMeasure : function(){
8706             var el = this.dom;
8707             if(el.offsetWidth || el.offsetHeight){
8708                 return this; // offsets work already
8709             }
8710             var changed = [];
8711             var p = this.dom, b = document.body; // start with this element
8712             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8713                 var pe = Roo.get(p);
8714                 if(pe.getStyle('display') == 'none'){
8715                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8716                     p.style.visibility = "hidden";
8717                     p.style.display = "block";
8718                 }
8719                 p = p.parentNode;
8720             }
8721             this._measureChanged = changed;
8722             return this;
8723
8724         },
8725
8726         /**
8727          * Restores displays to before beginMeasure was called
8728          * @return {Roo.Element} this
8729          */
8730         endMeasure : function(){
8731             var changed = this._measureChanged;
8732             if(changed){
8733                 for(var i = 0, len = changed.length; i < len; i++) {
8734                     var r = changed[i];
8735                     r.el.style.visibility = r.visibility;
8736                     r.el.style.display = "none";
8737                 }
8738                 this._measureChanged = null;
8739             }
8740             return this;
8741         },
8742
8743         /**
8744         * Update the innerHTML of this element, optionally searching for and processing scripts
8745         * @param {String} html The new HTML
8746         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8747         * @param {Function} callback For async script loading you can be noticed when the update completes
8748         * @return {Roo.Element} this
8749          */
8750         update : function(html, loadScripts, callback){
8751             if(typeof html == "undefined"){
8752                 html = "";
8753             }
8754             if(loadScripts !== true){
8755                 this.dom.innerHTML = html;
8756                 if(typeof callback == "function"){
8757                     callback();
8758                 }
8759                 return this;
8760             }
8761             var id = Roo.id();
8762             var dom = this.dom;
8763
8764             html += '<span id="' + id + '"></span>';
8765
8766             E.onAvailable(id, function(){
8767                 var hd = document.getElementsByTagName("head")[0];
8768                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8769                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8770                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8771
8772                 var match;
8773                 while(match = re.exec(html)){
8774                     var attrs = match[1];
8775                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8776                     if(srcMatch && srcMatch[2]){
8777                        var s = document.createElement("script");
8778                        s.src = srcMatch[2];
8779                        var typeMatch = attrs.match(typeRe);
8780                        if(typeMatch && typeMatch[2]){
8781                            s.type = typeMatch[2];
8782                        }
8783                        hd.appendChild(s);
8784                     }else if(match[2] && match[2].length > 0){
8785                         if(window.execScript) {
8786                            window.execScript(match[2]);
8787                         } else {
8788                             /**
8789                              * eval:var:id
8790                              * eval:var:dom
8791                              * eval:var:html
8792                              * 
8793                              */
8794                            window.eval(match[2]);
8795                         }
8796                     }
8797                 }
8798                 var el = document.getElementById(id);
8799                 if(el){el.parentNode.removeChild(el);}
8800                 if(typeof callback == "function"){
8801                     callback();
8802                 }
8803             });
8804             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8805             return this;
8806         },
8807
8808         /**
8809          * Direct access to the UpdateManager update() method (takes the same parameters).
8810          * @param {String/Function} url The url for this request or a function to call to get the url
8811          * @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}
8812          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8813          * @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.
8814          * @return {Roo.Element} this
8815          */
8816         load : function(){
8817             var um = this.getUpdateManager();
8818             um.update.apply(um, arguments);
8819             return this;
8820         },
8821
8822         /**
8823         * Gets this element's UpdateManager
8824         * @return {Roo.UpdateManager} The UpdateManager
8825         */
8826         getUpdateManager : function(){
8827             if(!this.updateManager){
8828                 this.updateManager = new Roo.UpdateManager(this);
8829             }
8830             return this.updateManager;
8831         },
8832
8833         /**
8834          * Disables text selection for this element (normalized across browsers)
8835          * @return {Roo.Element} this
8836          */
8837         unselectable : function(){
8838             this.dom.unselectable = "on";
8839             this.swallowEvent("selectstart", true);
8840             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8841             this.addClass("x-unselectable");
8842             return this;
8843         },
8844
8845         /**
8846         * Calculates the x, y to center this element on the screen
8847         * @return {Array} The x, y values [x, y]
8848         */
8849         getCenterXY : function(){
8850             return this.getAlignToXY(document, 'c-c');
8851         },
8852
8853         /**
8854         * Centers the Element in either the viewport, or another Element.
8855         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8856         */
8857         center : function(centerIn){
8858             this.alignTo(centerIn || document, 'c-c');
8859             return this;
8860         },
8861
8862         /**
8863          * Tests various css rules/browsers to determine if this element uses a border box
8864          * @return {Boolean}
8865          */
8866         isBorderBox : function(){
8867             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8868         },
8869
8870         /**
8871          * Return a box {x, y, width, height} that can be used to set another elements
8872          * size/location to match this element.
8873          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8874          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8875          * @return {Object} box An object in the format {x, y, width, height}
8876          */
8877         getBox : function(contentBox, local){
8878             var xy;
8879             if(!local){
8880                 xy = this.getXY();
8881             }else{
8882                 var left = parseInt(this.getStyle("left"), 10) || 0;
8883                 var top = parseInt(this.getStyle("top"), 10) || 0;
8884                 xy = [left, top];
8885             }
8886             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8887             if(!contentBox){
8888                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8889             }else{
8890                 var l = this.getBorderWidth("l")+this.getPadding("l");
8891                 var r = this.getBorderWidth("r")+this.getPadding("r");
8892                 var t = this.getBorderWidth("t")+this.getPadding("t");
8893                 var b = this.getBorderWidth("b")+this.getPadding("b");
8894                 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)};
8895             }
8896             bx.right = bx.x + bx.width;
8897             bx.bottom = bx.y + bx.height;
8898             return bx;
8899         },
8900
8901         /**
8902          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8903          for more information about the sides.
8904          * @param {String} sides
8905          * @return {Number}
8906          */
8907         getFrameWidth : function(sides, onlyContentBox){
8908             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8909         },
8910
8911         /**
8912          * 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.
8913          * @param {Object} box The box to fill {x, y, width, height}
8914          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8915          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8916          * @return {Roo.Element} this
8917          */
8918         setBox : function(box, adjust, animate){
8919             var w = box.width, h = box.height;
8920             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8921                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8922                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8923             }
8924             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8925             return this;
8926         },
8927
8928         /**
8929          * Forces the browser to repaint this element
8930          * @return {Roo.Element} this
8931          */
8932          repaint : function(){
8933             var dom = this.dom;
8934             this.addClass("x-repaint");
8935             setTimeout(function(){
8936                 Roo.get(dom).removeClass("x-repaint");
8937             }, 1);
8938             return this;
8939         },
8940
8941         /**
8942          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8943          * then it returns the calculated width of the sides (see getPadding)
8944          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8945          * @return {Object/Number}
8946          */
8947         getMargins : function(side){
8948             if(!side){
8949                 return {
8950                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8951                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8952                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8953                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8954                 };
8955             }else{
8956                 return this.addStyles(side, El.margins);
8957              }
8958         },
8959
8960         // private
8961         addStyles : function(sides, styles){
8962             var val = 0, v, w;
8963             for(var i = 0, len = sides.length; i < len; i++){
8964                 v = this.getStyle(styles[sides.charAt(i)]);
8965                 if(v){
8966                      w = parseInt(v, 10);
8967                      if(w){ val += w; }
8968                 }
8969             }
8970             return val;
8971         },
8972
8973         /**
8974          * Creates a proxy element of this element
8975          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8976          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8977          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8978          * @return {Roo.Element} The new proxy element
8979          */
8980         createProxy : function(config, renderTo, matchBox){
8981             if(renderTo){
8982                 renderTo = Roo.getDom(renderTo);
8983             }else{
8984                 renderTo = document.body;
8985             }
8986             config = typeof config == "object" ?
8987                 config : {tag : "div", cls: config};
8988             var proxy = Roo.DomHelper.append(renderTo, config, true);
8989             if(matchBox){
8990                proxy.setBox(this.getBox());
8991             }
8992             return proxy;
8993         },
8994
8995         /**
8996          * Puts a mask over this element to disable user interaction. Requires core.css.
8997          * This method can only be applied to elements which accept child nodes.
8998          * @param {String} msg (optional) A message to display in the mask
8999          * @param {String} msgCls (optional) A css class to apply to the msg element
9000          * @return {Element} The mask  element
9001          */
9002         mask : function(msg, msgCls)
9003         {
9004             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9005                 this.setStyle("position", "relative");
9006             }
9007             if(!this._mask){
9008                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9009             }
9010             this.addClass("x-masked");
9011             this._mask.setDisplayed(true);
9012             
9013             // we wander
9014             var z = 0;
9015             var dom = this.dom;
9016             while (dom && dom.style) {
9017                 if (!isNaN(parseInt(dom.style.zIndex))) {
9018                     z = Math.max(z, parseInt(dom.style.zIndex));
9019                 }
9020                 dom = dom.parentNode;
9021             }
9022             // if we are masking the body - then it hides everything..
9023             if (this.dom == document.body) {
9024                 z = 1000000;
9025                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9026                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9027             }
9028            
9029             if(typeof msg == 'string'){
9030                 if(!this._maskMsg){
9031                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9032                 }
9033                 var mm = this._maskMsg;
9034                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9035                 if (mm.dom.firstChild) { // weird IE issue?
9036                     mm.dom.firstChild.innerHTML = msg;
9037                 }
9038                 mm.setDisplayed(true);
9039                 mm.center(this);
9040                 mm.setStyle('z-index', z + 102);
9041             }
9042             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9043                 this._mask.setHeight(this.getHeight());
9044             }
9045             this._mask.setStyle('z-index', z + 100);
9046             
9047             return this._mask;
9048         },
9049
9050         /**
9051          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9052          * it is cached for reuse.
9053          */
9054         unmask : function(removeEl){
9055             if(this._mask){
9056                 if(removeEl === true){
9057                     this._mask.remove();
9058                     delete this._mask;
9059                     if(this._maskMsg){
9060                         this._maskMsg.remove();
9061                         delete this._maskMsg;
9062                     }
9063                 }else{
9064                     this._mask.setDisplayed(false);
9065                     if(this._maskMsg){
9066                         this._maskMsg.setDisplayed(false);
9067                     }
9068                 }
9069             }
9070             this.removeClass("x-masked");
9071         },
9072
9073         /**
9074          * Returns true if this element is masked
9075          * @return {Boolean}
9076          */
9077         isMasked : function(){
9078             return this._mask && this._mask.isVisible();
9079         },
9080
9081         /**
9082          * Creates an iframe shim for this element to keep selects and other windowed objects from
9083          * showing through.
9084          * @return {Roo.Element} The new shim element
9085          */
9086         createShim : function(){
9087             var el = document.createElement('iframe');
9088             el.frameBorder = 'no';
9089             el.className = 'roo-shim';
9090             if(Roo.isIE && Roo.isSecure){
9091                 el.src = Roo.SSL_SECURE_URL;
9092             }
9093             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9094             shim.autoBoxAdjust = false;
9095             return shim;
9096         },
9097
9098         /**
9099          * Removes this element from the DOM and deletes it from the cache
9100          */
9101         remove : function(){
9102             if(this.dom.parentNode){
9103                 this.dom.parentNode.removeChild(this.dom);
9104             }
9105             delete El.cache[this.dom.id];
9106         },
9107
9108         /**
9109          * Sets up event handlers to add and remove a css class when the mouse is over this element
9110          * @param {String} className
9111          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9112          * mouseout events for children elements
9113          * @return {Roo.Element} this
9114          */
9115         addClassOnOver : function(className, preventFlicker){
9116             this.on("mouseover", function(){
9117                 Roo.fly(this, '_internal').addClass(className);
9118             }, this.dom);
9119             var removeFn = function(e){
9120                 if(preventFlicker !== true || !e.within(this, true)){
9121                     Roo.fly(this, '_internal').removeClass(className);
9122                 }
9123             };
9124             this.on("mouseout", removeFn, this.dom);
9125             return this;
9126         },
9127
9128         /**
9129          * Sets up event handlers to add and remove a css class when this element has the focus
9130          * @param {String} className
9131          * @return {Roo.Element} this
9132          */
9133         addClassOnFocus : function(className){
9134             this.on("focus", function(){
9135                 Roo.fly(this, '_internal').addClass(className);
9136             }, this.dom);
9137             this.on("blur", function(){
9138                 Roo.fly(this, '_internal').removeClass(className);
9139             }, this.dom);
9140             return this;
9141         },
9142         /**
9143          * 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)
9144          * @param {String} className
9145          * @return {Roo.Element} this
9146          */
9147         addClassOnClick : function(className){
9148             var dom = this.dom;
9149             this.on("mousedown", function(){
9150                 Roo.fly(dom, '_internal').addClass(className);
9151                 var d = Roo.get(document);
9152                 var fn = function(){
9153                     Roo.fly(dom, '_internal').removeClass(className);
9154                     d.removeListener("mouseup", fn);
9155                 };
9156                 d.on("mouseup", fn);
9157             });
9158             return this;
9159         },
9160
9161         /**
9162          * Stops the specified event from bubbling and optionally prevents the default action
9163          * @param {String} eventName
9164          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9165          * @return {Roo.Element} this
9166          */
9167         swallowEvent : function(eventName, preventDefault){
9168             var fn = function(e){
9169                 e.stopPropagation();
9170                 if(preventDefault){
9171                     e.preventDefault();
9172                 }
9173             };
9174             if(eventName instanceof Array){
9175                 for(var i = 0, len = eventName.length; i < len; i++){
9176                      this.on(eventName[i], fn);
9177                 }
9178                 return this;
9179             }
9180             this.on(eventName, fn);
9181             return this;
9182         },
9183
9184         /**
9185          * @private
9186          */
9187       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9188
9189         /**
9190          * Sizes this element to its parent element's dimensions performing
9191          * neccessary box adjustments.
9192          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9193          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9194          * @return {Roo.Element} this
9195          */
9196         fitToParent : function(monitorResize, targetParent) {
9197           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9198           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9199           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9200             return;
9201           }
9202           var p = Roo.get(targetParent || this.dom.parentNode);
9203           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9204           if (monitorResize === true) {
9205             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9206             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9207           }
9208           return this;
9209         },
9210
9211         /**
9212          * Gets the next sibling, skipping text nodes
9213          * @return {HTMLElement} The next sibling or null
9214          */
9215         getNextSibling : function(){
9216             var n = this.dom.nextSibling;
9217             while(n && n.nodeType != 1){
9218                 n = n.nextSibling;
9219             }
9220             return n;
9221         },
9222
9223         /**
9224          * Gets the previous sibling, skipping text nodes
9225          * @return {HTMLElement} The previous sibling or null
9226          */
9227         getPrevSibling : function(){
9228             var n = this.dom.previousSibling;
9229             while(n && n.nodeType != 1){
9230                 n = n.previousSibling;
9231             }
9232             return n;
9233         },
9234
9235
9236         /**
9237          * Appends the passed element(s) to this element
9238          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9239          * @return {Roo.Element} this
9240          */
9241         appendChild: function(el){
9242             el = Roo.get(el);
9243             el.appendTo(this);
9244             return this;
9245         },
9246
9247         /**
9248          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9249          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9250          * automatically generated with the specified attributes.
9251          * @param {HTMLElement} insertBefore (optional) a child element of this element
9252          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9253          * @return {Roo.Element} The new child element
9254          */
9255         createChild: function(config, insertBefore, returnDom){
9256             config = config || {tag:'div'};
9257             if(insertBefore){
9258                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9259             }
9260             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9261         },
9262
9263         /**
9264          * Appends this element to the passed element
9265          * @param {String/HTMLElement/Element} el The new parent element
9266          * @return {Roo.Element} this
9267          */
9268         appendTo: function(el){
9269             el = Roo.getDom(el);
9270             el.appendChild(this.dom);
9271             return this;
9272         },
9273
9274         /**
9275          * Inserts this element before the passed element in the DOM
9276          * @param {String/HTMLElement/Element} el The element to insert before
9277          * @return {Roo.Element} this
9278          */
9279         insertBefore: function(el){
9280             el = Roo.getDom(el);
9281             el.parentNode.insertBefore(this.dom, el);
9282             return this;
9283         },
9284
9285         /**
9286          * Inserts this element after the passed element in the DOM
9287          * @param {String/HTMLElement/Element} el The element to insert after
9288          * @return {Roo.Element} this
9289          */
9290         insertAfter: function(el){
9291             el = Roo.getDom(el);
9292             el.parentNode.insertBefore(this.dom, el.nextSibling);
9293             return this;
9294         },
9295
9296         /**
9297          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9298          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9299          * @return {Roo.Element} The new child
9300          */
9301         insertFirst: function(el, returnDom){
9302             el = el || {};
9303             if(typeof el == 'object' && !el.nodeType){ // dh config
9304                 return this.createChild(el, this.dom.firstChild, returnDom);
9305             }else{
9306                 el = Roo.getDom(el);
9307                 this.dom.insertBefore(el, this.dom.firstChild);
9308                 return !returnDom ? Roo.get(el) : el;
9309             }
9310         },
9311
9312         /**
9313          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9314          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9315          * @param {String} where (optional) 'before' or 'after' defaults to before
9316          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9317          * @return {Roo.Element} the inserted Element
9318          */
9319         insertSibling: function(el, where, returnDom){
9320             where = where ? where.toLowerCase() : 'before';
9321             el = el || {};
9322             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9323
9324             if(typeof el == 'object' && !el.nodeType){ // dh config
9325                 if(where == 'after' && !this.dom.nextSibling){
9326                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9327                 }else{
9328                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9329                 }
9330
9331             }else{
9332                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9333                             where == 'before' ? this.dom : this.dom.nextSibling);
9334                 if(!returnDom){
9335                     rt = Roo.get(rt);
9336                 }
9337             }
9338             return rt;
9339         },
9340
9341         /**
9342          * Creates and wraps this element with another element
9343          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9344          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9345          * @return {HTMLElement/Element} The newly created wrapper element
9346          */
9347         wrap: function(config, returnDom){
9348             if(!config){
9349                 config = {tag: "div"};
9350             }
9351             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9352             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9353             return newEl;
9354         },
9355
9356         /**
9357          * Replaces the passed element with this element
9358          * @param {String/HTMLElement/Element} el The element to replace
9359          * @return {Roo.Element} this
9360          */
9361         replace: function(el){
9362             el = Roo.get(el);
9363             this.insertBefore(el);
9364             el.remove();
9365             return this;
9366         },
9367
9368         /**
9369          * Inserts an html fragment into this element
9370          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9371          * @param {String} html The HTML fragment
9372          * @param {Boolean} returnEl True to return an Roo.Element
9373          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9374          */
9375         insertHtml : function(where, html, returnEl){
9376             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9377             return returnEl ? Roo.get(el) : el;
9378         },
9379
9380         /**
9381          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9382          * @param {Object} o The object with the attributes
9383          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9384          * @return {Roo.Element} this
9385          */
9386         set : function(o, useSet){
9387             var el = this.dom;
9388             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9389             for(var attr in o){
9390                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9391                 if(attr=="cls"){
9392                     el.className = o["cls"];
9393                 }else{
9394                     if(useSet) {
9395                         el.setAttribute(attr, o[attr]);
9396                     } else {
9397                         el[attr] = o[attr];
9398                     }
9399                 }
9400             }
9401             if(o.style){
9402                 Roo.DomHelper.applyStyles(el, o.style);
9403             }
9404             return this;
9405         },
9406
9407         /**
9408          * Convenience method for constructing a KeyMap
9409          * @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:
9410          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9411          * @param {Function} fn The function to call
9412          * @param {Object} scope (optional) The scope of the function
9413          * @return {Roo.KeyMap} The KeyMap created
9414          */
9415         addKeyListener : function(key, fn, scope){
9416             var config;
9417             if(typeof key != "object" || key instanceof Array){
9418                 config = {
9419                     key: key,
9420                     fn: fn,
9421                     scope: scope
9422                 };
9423             }else{
9424                 config = {
9425                     key : key.key,
9426                     shift : key.shift,
9427                     ctrl : key.ctrl,
9428                     alt : key.alt,
9429                     fn: fn,
9430                     scope: scope
9431                 };
9432             }
9433             return new Roo.KeyMap(this, config);
9434         },
9435
9436         /**
9437          * Creates a KeyMap for this element
9438          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9439          * @return {Roo.KeyMap} The KeyMap created
9440          */
9441         addKeyMap : function(config){
9442             return new Roo.KeyMap(this, config);
9443         },
9444
9445         /**
9446          * Returns true if this element is scrollable.
9447          * @return {Boolean}
9448          */
9449          isScrollable : function(){
9450             var dom = this.dom;
9451             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9452         },
9453
9454         /**
9455          * 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().
9456          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9457          * @param {Number} value The new scroll value
9458          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9459          * @return {Element} this
9460          */
9461
9462         scrollTo : function(side, value, animate){
9463             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9464             if(!animate || !A){
9465                 this.dom[prop] = value;
9466             }else{
9467                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9468                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9469             }
9470             return this;
9471         },
9472
9473         /**
9474          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9475          * within this element's scrollable range.
9476          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9477          * @param {Number} distance How far to scroll the element in pixels
9478          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9479          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9480          * was scrolled as far as it could go.
9481          */
9482          scroll : function(direction, distance, animate){
9483              if(!this.isScrollable()){
9484                  return;
9485              }
9486              var el = this.dom;
9487              var l = el.scrollLeft, t = el.scrollTop;
9488              var w = el.scrollWidth, h = el.scrollHeight;
9489              var cw = el.clientWidth, ch = el.clientHeight;
9490              direction = direction.toLowerCase();
9491              var scrolled = false;
9492              var a = this.preanim(arguments, 2);
9493              switch(direction){
9494                  case "l":
9495                  case "left":
9496                      if(w - l > cw){
9497                          var v = Math.min(l + distance, w-cw);
9498                          this.scrollTo("left", v, a);
9499                          scrolled = true;
9500                      }
9501                      break;
9502                 case "r":
9503                 case "right":
9504                      if(l > 0){
9505                          var v = Math.max(l - distance, 0);
9506                          this.scrollTo("left", v, a);
9507                          scrolled = true;
9508                      }
9509                      break;
9510                 case "t":
9511                 case "top":
9512                 case "up":
9513                      if(t > 0){
9514                          var v = Math.max(t - distance, 0);
9515                          this.scrollTo("top", v, a);
9516                          scrolled = true;
9517                      }
9518                      break;
9519                 case "b":
9520                 case "bottom":
9521                 case "down":
9522                      if(h - t > ch){
9523                          var v = Math.min(t + distance, h-ch);
9524                          this.scrollTo("top", v, a);
9525                          scrolled = true;
9526                      }
9527                      break;
9528              }
9529              return scrolled;
9530         },
9531
9532         /**
9533          * Translates the passed page coordinates into left/top css values for this element
9534          * @param {Number/Array} x The page x or an array containing [x, y]
9535          * @param {Number} y The page y
9536          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9537          */
9538         translatePoints : function(x, y){
9539             if(typeof x == 'object' || x instanceof Array){
9540                 y = x[1]; x = x[0];
9541             }
9542             var p = this.getStyle('position');
9543             var o = this.getXY();
9544
9545             var l = parseInt(this.getStyle('left'), 10);
9546             var t = parseInt(this.getStyle('top'), 10);
9547
9548             if(isNaN(l)){
9549                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9550             }
9551             if(isNaN(t)){
9552                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9553             }
9554
9555             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9556         },
9557
9558         /**
9559          * Returns the current scroll position of the element.
9560          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9561          */
9562         getScroll : function(){
9563             var d = this.dom, doc = document;
9564             if(d == doc || d == doc.body){
9565                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9566                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9567                 return {left: l, top: t};
9568             }else{
9569                 return {left: d.scrollLeft, top: d.scrollTop};
9570             }
9571         },
9572
9573         /**
9574          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9575          * are convert to standard 6 digit hex color.
9576          * @param {String} attr The css attribute
9577          * @param {String} defaultValue The default value to use when a valid color isn't found
9578          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9579          * YUI color anims.
9580          */
9581         getColor : function(attr, defaultValue, prefix){
9582             var v = this.getStyle(attr);
9583             if(!v || v == "transparent" || v == "inherit") {
9584                 return defaultValue;
9585             }
9586             var color = typeof prefix == "undefined" ? "#" : prefix;
9587             if(v.substr(0, 4) == "rgb("){
9588                 var rvs = v.slice(4, v.length -1).split(",");
9589                 for(var i = 0; i < 3; i++){
9590                     var h = parseInt(rvs[i]).toString(16);
9591                     if(h < 16){
9592                         h = "0" + h;
9593                     }
9594                     color += h;
9595                 }
9596             } else {
9597                 if(v.substr(0, 1) == "#"){
9598                     if(v.length == 4) {
9599                         for(var i = 1; i < 4; i++){
9600                             var c = v.charAt(i);
9601                             color +=  c + c;
9602                         }
9603                     }else if(v.length == 7){
9604                         color += v.substr(1);
9605                     }
9606                 }
9607             }
9608             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9609         },
9610
9611         /**
9612          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9613          * gradient background, rounded corners and a 4-way shadow.
9614          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9615          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9616          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9617          * @return {Roo.Element} this
9618          */
9619         boxWrap : function(cls){
9620             cls = cls || 'x-box';
9621             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9622             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9623             return el;
9624         },
9625
9626         /**
9627          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9628          * @param {String} namespace The namespace in which to look for the attribute
9629          * @param {String} name The attribute name
9630          * @return {String} The attribute value
9631          */
9632         getAttributeNS : Roo.isIE ? function(ns, name){
9633             var d = this.dom;
9634             var type = typeof d[ns+":"+name];
9635             if(type != 'undefined' && type != 'unknown'){
9636                 return d[ns+":"+name];
9637             }
9638             return d[name];
9639         } : function(ns, name){
9640             var d = this.dom;
9641             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9642         },
9643         
9644         
9645         /**
9646          * Sets or Returns the value the dom attribute value
9647          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9648          * @param {String} value (optional) The value to set the attribute to
9649          * @return {String} The attribute value
9650          */
9651         attr : function(name){
9652             if (arguments.length > 1) {
9653                 this.dom.setAttribute(name, arguments[1]);
9654                 return arguments[1];
9655             }
9656             if (typeof(name) == 'object') {
9657                 for(var i in name) {
9658                     this.attr(i, name[i]);
9659                 }
9660                 return name;
9661             }
9662             
9663             
9664             if (!this.dom.hasAttribute(name)) {
9665                 return undefined;
9666             }
9667             return this.dom.getAttribute(name);
9668         }
9669         
9670         
9671         
9672     };
9673
9674     var ep = El.prototype;
9675
9676     /**
9677      * Appends an event handler (Shorthand for addListener)
9678      * @param {String}   eventName     The type of event to append
9679      * @param {Function} fn        The method the event invokes
9680      * @param {Object} scope       (optional) The scope (this object) of the fn
9681      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9682      * @method
9683      */
9684     ep.on = ep.addListener;
9685         // backwards compat
9686     ep.mon = ep.addListener;
9687
9688     /**
9689      * Removes an event handler from this element (shorthand for removeListener)
9690      * @param {String} eventName the type of event to remove
9691      * @param {Function} fn the method the event invokes
9692      * @return {Roo.Element} this
9693      * @method
9694      */
9695     ep.un = ep.removeListener;
9696
9697     /**
9698      * true to automatically adjust width and height settings for box-model issues (default to true)
9699      */
9700     ep.autoBoxAdjust = true;
9701
9702     // private
9703     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9704
9705     // private
9706     El.addUnits = function(v, defaultUnit){
9707         if(v === "" || v == "auto"){
9708             return v;
9709         }
9710         if(v === undefined){
9711             return '';
9712         }
9713         if(typeof v == "number" || !El.unitPattern.test(v)){
9714             return v + (defaultUnit || 'px');
9715         }
9716         return v;
9717     };
9718
9719     // special markup used throughout Roo when box wrapping elements
9720     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>';
9721     /**
9722      * Visibility mode constant - Use visibility to hide element
9723      * @static
9724      * @type Number
9725      */
9726     El.VISIBILITY = 1;
9727     /**
9728      * Visibility mode constant - Use display to hide element
9729      * @static
9730      * @type Number
9731      */
9732     El.DISPLAY = 2;
9733
9734     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9735     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9736     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9737
9738
9739
9740     /**
9741      * @private
9742      */
9743     El.cache = {};
9744
9745     var docEl;
9746
9747     /**
9748      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9749      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9750      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9751      * @return {Element} The Element object
9752      * @static
9753      */
9754     El.get = function(el){
9755         var ex, elm, id;
9756         if(!el){ return null; }
9757         if(typeof el == "string"){ // element id
9758             if(!(elm = document.getElementById(el))){
9759                 return null;
9760             }
9761             if(ex = El.cache[el]){
9762                 ex.dom = elm;
9763             }else{
9764                 ex = El.cache[el] = new El(elm);
9765             }
9766             return ex;
9767         }else if(el.tagName){ // dom element
9768             if(!(id = el.id)){
9769                 id = Roo.id(el);
9770             }
9771             if(ex = El.cache[id]){
9772                 ex.dom = el;
9773             }else{
9774                 ex = El.cache[id] = new El(el);
9775             }
9776             return ex;
9777         }else if(el instanceof El){
9778             if(el != docEl){
9779                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9780                                                               // catch case where it hasn't been appended
9781                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9782             }
9783             return el;
9784         }else if(el.isComposite){
9785             return el;
9786         }else if(el instanceof Array){
9787             return El.select(el);
9788         }else if(el == document){
9789             // create a bogus element object representing the document object
9790             if(!docEl){
9791                 var f = function(){};
9792                 f.prototype = El.prototype;
9793                 docEl = new f();
9794                 docEl.dom = document;
9795             }
9796             return docEl;
9797         }
9798         return null;
9799     };
9800
9801     // private
9802     El.uncache = function(el){
9803         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9804             if(a[i]){
9805                 delete El.cache[a[i].id || a[i]];
9806             }
9807         }
9808     };
9809
9810     // private
9811     // Garbage collection - uncache elements/purge listeners on orphaned elements
9812     // so we don't hold a reference and cause the browser to retain them
9813     El.garbageCollect = function(){
9814         if(!Roo.enableGarbageCollector){
9815             clearInterval(El.collectorThread);
9816             return;
9817         }
9818         for(var eid in El.cache){
9819             var el = El.cache[eid], d = el.dom;
9820             // -------------------------------------------------------
9821             // Determining what is garbage:
9822             // -------------------------------------------------------
9823             // !d
9824             // dom node is null, definitely garbage
9825             // -------------------------------------------------------
9826             // !d.parentNode
9827             // no parentNode == direct orphan, definitely garbage
9828             // -------------------------------------------------------
9829             // !d.offsetParent && !document.getElementById(eid)
9830             // display none elements have no offsetParent so we will
9831             // also try to look it up by it's id. However, check
9832             // offsetParent first so we don't do unneeded lookups.
9833             // This enables collection of elements that are not orphans
9834             // directly, but somewhere up the line they have an orphan
9835             // parent.
9836             // -------------------------------------------------------
9837             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9838                 delete El.cache[eid];
9839                 if(d && Roo.enableListenerCollection){
9840                     E.purgeElement(d);
9841                 }
9842             }
9843         }
9844     }
9845     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9846
9847
9848     // dom is optional
9849     El.Flyweight = function(dom){
9850         this.dom = dom;
9851     };
9852     El.Flyweight.prototype = El.prototype;
9853
9854     El._flyweights = {};
9855     /**
9856      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9857      * the dom node can be overwritten by other code.
9858      * @param {String/HTMLElement} el The dom node or id
9859      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9860      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9861      * @static
9862      * @return {Element} The shared Element object
9863      */
9864     El.fly = function(el, named){
9865         named = named || '_global';
9866         el = Roo.getDom(el);
9867         if(!el){
9868             return null;
9869         }
9870         if(!El._flyweights[named]){
9871             El._flyweights[named] = new El.Flyweight();
9872         }
9873         El._flyweights[named].dom = el;
9874         return El._flyweights[named];
9875     };
9876
9877     /**
9878      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9879      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9880      * Shorthand of {@link Roo.Element#get}
9881      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9882      * @return {Element} The Element object
9883      * @member Roo
9884      * @method get
9885      */
9886     Roo.get = El.get;
9887     /**
9888      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9889      * the dom node can be overwritten by other code.
9890      * Shorthand of {@link Roo.Element#fly}
9891      * @param {String/HTMLElement} el The dom node or id
9892      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9893      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9894      * @static
9895      * @return {Element} The shared Element object
9896      * @member Roo
9897      * @method fly
9898      */
9899     Roo.fly = El.fly;
9900
9901     // speedy lookup for elements never to box adjust
9902     var noBoxAdjust = Roo.isStrict ? {
9903         select:1
9904     } : {
9905         input:1, select:1, textarea:1
9906     };
9907     if(Roo.isIE || Roo.isGecko){
9908         noBoxAdjust['button'] = 1;
9909     }
9910
9911
9912     Roo.EventManager.on(window, 'unload', function(){
9913         delete El.cache;
9914         delete El._flyweights;
9915     });
9916 })();
9917
9918
9919
9920
9921 if(Roo.DomQuery){
9922     Roo.Element.selectorFunction = Roo.DomQuery.select;
9923 }
9924
9925 Roo.Element.select = function(selector, unique, root){
9926     var els;
9927     if(typeof selector == "string"){
9928         els = Roo.Element.selectorFunction(selector, root);
9929     }else if(selector.length !== undefined){
9930         els = selector;
9931     }else{
9932         throw "Invalid selector";
9933     }
9934     if(unique === true){
9935         return new Roo.CompositeElement(els);
9936     }else{
9937         return new Roo.CompositeElementLite(els);
9938     }
9939 };
9940 /**
9941  * Selects elements based on the passed CSS selector to enable working on them as 1.
9942  * @param {String/Array} selector The CSS selector or an array of elements
9943  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9944  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9945  * @return {CompositeElementLite/CompositeElement}
9946  * @member Roo
9947  * @method select
9948  */
9949 Roo.select = Roo.Element.select;
9950
9951
9952
9953
9954
9955
9956
9957
9958
9959
9960
9961
9962
9963
9964 /*
9965  * Based on:
9966  * Ext JS Library 1.1.1
9967  * Copyright(c) 2006-2007, Ext JS, LLC.
9968  *
9969  * Originally Released Under LGPL - original licence link has changed is not relivant.
9970  *
9971  * Fork - LGPL
9972  * <script type="text/javascript">
9973  */
9974
9975
9976
9977 //Notifies Element that fx methods are available
9978 Roo.enableFx = true;
9979
9980 /**
9981  * @class Roo.Fx
9982  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9983  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9984  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9985  * Element effects to work.</p><br/>
9986  *
9987  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9988  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9989  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9990  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9991  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9992  * expected results and should be done with care.</p><br/>
9993  *
9994  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9995  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9996 <pre>
9997 Value  Description
9998 -----  -----------------------------
9999 tl     The top left corner
10000 t      The center of the top edge
10001 tr     The top right corner
10002 l      The center of the left edge
10003 r      The center of the right edge
10004 bl     The bottom left corner
10005 b      The center of the bottom edge
10006 br     The bottom right corner
10007 </pre>
10008  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10009  * below are common options that can be passed to any Fx method.</b>
10010  * @cfg {Function} callback A function called when the effect is finished
10011  * @cfg {Object} scope The scope of the effect function
10012  * @cfg {String} easing A valid Easing value for the effect
10013  * @cfg {String} afterCls A css class to apply after the effect
10014  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10015  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10016  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10017  * effects that end with the element being visually hidden, ignored otherwise)
10018  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10019  * a function which returns such a specification that will be applied to the Element after the effect finishes
10020  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10021  * @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
10022  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10023  */
10024 Roo.Fx = {
10025         /**
10026          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10027          * origin for the slide effect.  This function automatically handles wrapping the element with
10028          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10029          * Usage:
10030          *<pre><code>
10031 // default: slide the element in from the top
10032 el.slideIn();
10033
10034 // custom: slide the element in from the right with a 2-second duration
10035 el.slideIn('r', { duration: 2 });
10036
10037 // common config options shown with default values
10038 el.slideIn('t', {
10039     easing: 'easeOut',
10040     duration: .5
10041 });
10042 </code></pre>
10043          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10044          * @param {Object} options (optional) Object literal with any of the Fx config options
10045          * @return {Roo.Element} The Element
10046          */
10047     slideIn : function(anchor, o){
10048         var el = this.getFxEl();
10049         o = o || {};
10050
10051         el.queueFx(o, function(){
10052
10053             anchor = anchor || "t";
10054
10055             // fix display to visibility
10056             this.fixDisplay();
10057
10058             // restore values after effect
10059             var r = this.getFxRestore();
10060             var b = this.getBox();
10061             // fixed size for slide
10062             this.setSize(b);
10063
10064             // wrap if needed
10065             var wrap = this.fxWrap(r.pos, o, "hidden");
10066
10067             var st = this.dom.style;
10068             st.visibility = "visible";
10069             st.position = "absolute";
10070
10071             // clear out temp styles after slide and unwrap
10072             var after = function(){
10073                 el.fxUnwrap(wrap, r.pos, o);
10074                 st.width = r.width;
10075                 st.height = r.height;
10076                 el.afterFx(o);
10077             };
10078             // time to calc the positions
10079             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10080
10081             switch(anchor.toLowerCase()){
10082                 case "t":
10083                     wrap.setSize(b.width, 0);
10084                     st.left = st.bottom = "0";
10085                     a = {height: bh};
10086                 break;
10087                 case "l":
10088                     wrap.setSize(0, b.height);
10089                     st.right = st.top = "0";
10090                     a = {width: bw};
10091                 break;
10092                 case "r":
10093                     wrap.setSize(0, b.height);
10094                     wrap.setX(b.right);
10095                     st.left = st.top = "0";
10096                     a = {width: bw, points: pt};
10097                 break;
10098                 case "b":
10099                     wrap.setSize(b.width, 0);
10100                     wrap.setY(b.bottom);
10101                     st.left = st.top = "0";
10102                     a = {height: bh, points: pt};
10103                 break;
10104                 case "tl":
10105                     wrap.setSize(0, 0);
10106                     st.right = st.bottom = "0";
10107                     a = {width: bw, height: bh};
10108                 break;
10109                 case "bl":
10110                     wrap.setSize(0, 0);
10111                     wrap.setY(b.y+b.height);
10112                     st.right = st.top = "0";
10113                     a = {width: bw, height: bh, points: pt};
10114                 break;
10115                 case "br":
10116                     wrap.setSize(0, 0);
10117                     wrap.setXY([b.right, b.bottom]);
10118                     st.left = st.top = "0";
10119                     a = {width: bw, height: bh, points: pt};
10120                 break;
10121                 case "tr":
10122                     wrap.setSize(0, 0);
10123                     wrap.setX(b.x+b.width);
10124                     st.left = st.bottom = "0";
10125                     a = {width: bw, height: bh, points: pt};
10126                 break;
10127             }
10128             this.dom.style.visibility = "visible";
10129             wrap.show();
10130
10131             arguments.callee.anim = wrap.fxanim(a,
10132                 o,
10133                 'motion',
10134                 .5,
10135                 'easeOut', after);
10136         });
10137         return this;
10138     },
10139     
10140         /**
10141          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10142          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10143          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10144          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10145          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10146          * Usage:
10147          *<pre><code>
10148 // default: slide the element out to the top
10149 el.slideOut();
10150
10151 // custom: slide the element out to the right with a 2-second duration
10152 el.slideOut('r', { duration: 2 });
10153
10154 // common config options shown with default values
10155 el.slideOut('t', {
10156     easing: 'easeOut',
10157     duration: .5,
10158     remove: false,
10159     useDisplay: false
10160 });
10161 </code></pre>
10162          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10163          * @param {Object} options (optional) Object literal with any of the Fx config options
10164          * @return {Roo.Element} The Element
10165          */
10166     slideOut : function(anchor, o){
10167         var el = this.getFxEl();
10168         o = o || {};
10169
10170         el.queueFx(o, function(){
10171
10172             anchor = anchor || "t";
10173
10174             // restore values after effect
10175             var r = this.getFxRestore();
10176             
10177             var b = this.getBox();
10178             // fixed size for slide
10179             this.setSize(b);
10180
10181             // wrap if needed
10182             var wrap = this.fxWrap(r.pos, o, "visible");
10183
10184             var st = this.dom.style;
10185             st.visibility = "visible";
10186             st.position = "absolute";
10187
10188             wrap.setSize(b);
10189
10190             var after = function(){
10191                 if(o.useDisplay){
10192                     el.setDisplayed(false);
10193                 }else{
10194                     el.hide();
10195                 }
10196
10197                 el.fxUnwrap(wrap, r.pos, o);
10198
10199                 st.width = r.width;
10200                 st.height = r.height;
10201
10202                 el.afterFx(o);
10203             };
10204
10205             var a, zero = {to: 0};
10206             switch(anchor.toLowerCase()){
10207                 case "t":
10208                     st.left = st.bottom = "0";
10209                     a = {height: zero};
10210                 break;
10211                 case "l":
10212                     st.right = st.top = "0";
10213                     a = {width: zero};
10214                 break;
10215                 case "r":
10216                     st.left = st.top = "0";
10217                     a = {width: zero, points: {to:[b.right, b.y]}};
10218                 break;
10219                 case "b":
10220                     st.left = st.top = "0";
10221                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10222                 break;
10223                 case "tl":
10224                     st.right = st.bottom = "0";
10225                     a = {width: zero, height: zero};
10226                 break;
10227                 case "bl":
10228                     st.right = st.top = "0";
10229                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10230                 break;
10231                 case "br":
10232                     st.left = st.top = "0";
10233                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10234                 break;
10235                 case "tr":
10236                     st.left = st.bottom = "0";
10237                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10238                 break;
10239             }
10240
10241             arguments.callee.anim = wrap.fxanim(a,
10242                 o,
10243                 'motion',
10244                 .5,
10245                 "easeOut", after);
10246         });
10247         return this;
10248     },
10249
10250         /**
10251          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10252          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10253          * The element must be removed from the DOM using the 'remove' config option if desired.
10254          * Usage:
10255          *<pre><code>
10256 // default
10257 el.puff();
10258
10259 // common config options shown with default values
10260 el.puff({
10261     easing: 'easeOut',
10262     duration: .5,
10263     remove: false,
10264     useDisplay: false
10265 });
10266 </code></pre>
10267          * @param {Object} options (optional) Object literal with any of the Fx config options
10268          * @return {Roo.Element} The Element
10269          */
10270     puff : function(o){
10271         var el = this.getFxEl();
10272         o = o || {};
10273
10274         el.queueFx(o, function(){
10275             this.clearOpacity();
10276             this.show();
10277
10278             // restore values after effect
10279             var r = this.getFxRestore();
10280             var st = this.dom.style;
10281
10282             var after = function(){
10283                 if(o.useDisplay){
10284                     el.setDisplayed(false);
10285                 }else{
10286                     el.hide();
10287                 }
10288
10289                 el.clearOpacity();
10290
10291                 el.setPositioning(r.pos);
10292                 st.width = r.width;
10293                 st.height = r.height;
10294                 st.fontSize = '';
10295                 el.afterFx(o);
10296             };
10297
10298             var width = this.getWidth();
10299             var height = this.getHeight();
10300
10301             arguments.callee.anim = this.fxanim({
10302                     width : {to: this.adjustWidth(width * 2)},
10303                     height : {to: this.adjustHeight(height * 2)},
10304                     points : {by: [-(width * .5), -(height * .5)]},
10305                     opacity : {to: 0},
10306                     fontSize: {to:200, unit: "%"}
10307                 },
10308                 o,
10309                 'motion',
10310                 .5,
10311                 "easeOut", after);
10312         });
10313         return this;
10314     },
10315
10316         /**
10317          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10318          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10319          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10320          * Usage:
10321          *<pre><code>
10322 // default
10323 el.switchOff();
10324
10325 // all config options shown with default values
10326 el.switchOff({
10327     easing: 'easeIn',
10328     duration: .3,
10329     remove: false,
10330     useDisplay: false
10331 });
10332 </code></pre>
10333          * @param {Object} options (optional) Object literal with any of the Fx config options
10334          * @return {Roo.Element} The Element
10335          */
10336     switchOff : function(o){
10337         var el = this.getFxEl();
10338         o = o || {};
10339
10340         el.queueFx(o, function(){
10341             this.clearOpacity();
10342             this.clip();
10343
10344             // restore values after effect
10345             var r = this.getFxRestore();
10346             var st = this.dom.style;
10347
10348             var after = function(){
10349                 if(o.useDisplay){
10350                     el.setDisplayed(false);
10351                 }else{
10352                     el.hide();
10353                 }
10354
10355                 el.clearOpacity();
10356                 el.setPositioning(r.pos);
10357                 st.width = r.width;
10358                 st.height = r.height;
10359
10360                 el.afterFx(o);
10361             };
10362
10363             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10364                 this.clearOpacity();
10365                 (function(){
10366                     this.fxanim({
10367                         height:{to:1},
10368                         points:{by:[0, this.getHeight() * .5]}
10369                     }, o, 'motion', 0.3, 'easeIn', after);
10370                 }).defer(100, this);
10371             });
10372         });
10373         return this;
10374     },
10375
10376     /**
10377      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10378      * changed using the "attr" config option) and then fading back to the original color. If no original
10379      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10380      * Usage:
10381 <pre><code>
10382 // default: highlight background to yellow
10383 el.highlight();
10384
10385 // custom: highlight foreground text to blue for 2 seconds
10386 el.highlight("0000ff", { attr: 'color', duration: 2 });
10387
10388 // common config options shown with default values
10389 el.highlight("ffff9c", {
10390     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10391     endColor: (current color) or "ffffff",
10392     easing: 'easeIn',
10393     duration: 1
10394 });
10395 </code></pre>
10396      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10397      * @param {Object} options (optional) Object literal with any of the Fx config options
10398      * @return {Roo.Element} The Element
10399      */ 
10400     highlight : function(color, o){
10401         var el = this.getFxEl();
10402         o = o || {};
10403
10404         el.queueFx(o, function(){
10405             color = color || "ffff9c";
10406             attr = o.attr || "backgroundColor";
10407
10408             this.clearOpacity();
10409             this.show();
10410
10411             var origColor = this.getColor(attr);
10412             var restoreColor = this.dom.style[attr];
10413             endColor = (o.endColor || origColor) || "ffffff";
10414
10415             var after = function(){
10416                 el.dom.style[attr] = restoreColor;
10417                 el.afterFx(o);
10418             };
10419
10420             var a = {};
10421             a[attr] = {from: color, to: endColor};
10422             arguments.callee.anim = this.fxanim(a,
10423                 o,
10424                 'color',
10425                 1,
10426                 'easeIn', after);
10427         });
10428         return this;
10429     },
10430
10431    /**
10432     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10433     * Usage:
10434 <pre><code>
10435 // default: a single light blue ripple
10436 el.frame();
10437
10438 // custom: 3 red ripples lasting 3 seconds total
10439 el.frame("ff0000", 3, { duration: 3 });
10440
10441 // common config options shown with default values
10442 el.frame("C3DAF9", 1, {
10443     duration: 1 //duration of entire animation (not each individual ripple)
10444     // Note: Easing is not configurable and will be ignored if included
10445 });
10446 </code></pre>
10447     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10448     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10449     * @param {Object} options (optional) Object literal with any of the Fx config options
10450     * @return {Roo.Element} The Element
10451     */
10452     frame : function(color, count, o){
10453         var el = this.getFxEl();
10454         o = o || {};
10455
10456         el.queueFx(o, function(){
10457             color = color || "#C3DAF9";
10458             if(color.length == 6){
10459                 color = "#" + color;
10460             }
10461             count = count || 1;
10462             duration = o.duration || 1;
10463             this.show();
10464
10465             var b = this.getBox();
10466             var animFn = function(){
10467                 var proxy = this.createProxy({
10468
10469                      style:{
10470                         visbility:"hidden",
10471                         position:"absolute",
10472                         "z-index":"35000", // yee haw
10473                         border:"0px solid " + color
10474                      }
10475                   });
10476                 var scale = Roo.isBorderBox ? 2 : 1;
10477                 proxy.animate({
10478                     top:{from:b.y, to:b.y - 20},
10479                     left:{from:b.x, to:b.x - 20},
10480                     borderWidth:{from:0, to:10},
10481                     opacity:{from:1, to:0},
10482                     height:{from:b.height, to:(b.height + (20*scale))},
10483                     width:{from:b.width, to:(b.width + (20*scale))}
10484                 }, duration, function(){
10485                     proxy.remove();
10486                 });
10487                 if(--count > 0){
10488                      animFn.defer((duration/2)*1000, this);
10489                 }else{
10490                     el.afterFx(o);
10491                 }
10492             };
10493             animFn.call(this);
10494         });
10495         return this;
10496     },
10497
10498    /**
10499     * Creates a pause before any subsequent queued effects begin.  If there are
10500     * no effects queued after the pause it will have no effect.
10501     * Usage:
10502 <pre><code>
10503 el.pause(1);
10504 </code></pre>
10505     * @param {Number} seconds The length of time to pause (in seconds)
10506     * @return {Roo.Element} The Element
10507     */
10508     pause : function(seconds){
10509         var el = this.getFxEl();
10510         var o = {};
10511
10512         el.queueFx(o, function(){
10513             setTimeout(function(){
10514                 el.afterFx(o);
10515             }, seconds * 1000);
10516         });
10517         return this;
10518     },
10519
10520    /**
10521     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10522     * using the "endOpacity" config option.
10523     * Usage:
10524 <pre><code>
10525 // default: fade in from opacity 0 to 100%
10526 el.fadeIn();
10527
10528 // custom: fade in from opacity 0 to 75% over 2 seconds
10529 el.fadeIn({ endOpacity: .75, duration: 2});
10530
10531 // common config options shown with default values
10532 el.fadeIn({
10533     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10534     easing: 'easeOut',
10535     duration: .5
10536 });
10537 </code></pre>
10538     * @param {Object} options (optional) Object literal with any of the Fx config options
10539     * @return {Roo.Element} The Element
10540     */
10541     fadeIn : function(o){
10542         var el = this.getFxEl();
10543         o = o || {};
10544         el.queueFx(o, function(){
10545             this.setOpacity(0);
10546             this.fixDisplay();
10547             this.dom.style.visibility = 'visible';
10548             var to = o.endOpacity || 1;
10549             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10550                 o, null, .5, "easeOut", function(){
10551                 if(to == 1){
10552                     this.clearOpacity();
10553                 }
10554                 el.afterFx(o);
10555             });
10556         });
10557         return this;
10558     },
10559
10560    /**
10561     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10562     * using the "endOpacity" config option.
10563     * Usage:
10564 <pre><code>
10565 // default: fade out from the element's current opacity to 0
10566 el.fadeOut();
10567
10568 // custom: fade out from the element's current opacity to 25% over 2 seconds
10569 el.fadeOut({ endOpacity: .25, duration: 2});
10570
10571 // common config options shown with default values
10572 el.fadeOut({
10573     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10574     easing: 'easeOut',
10575     duration: .5
10576     remove: false,
10577     useDisplay: false
10578 });
10579 </code></pre>
10580     * @param {Object} options (optional) Object literal with any of the Fx config options
10581     * @return {Roo.Element} The Element
10582     */
10583     fadeOut : function(o){
10584         var el = this.getFxEl();
10585         o = o || {};
10586         el.queueFx(o, function(){
10587             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10588                 o, null, .5, "easeOut", function(){
10589                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10590                      this.dom.style.display = "none";
10591                 }else{
10592                      this.dom.style.visibility = "hidden";
10593                 }
10594                 this.clearOpacity();
10595                 el.afterFx(o);
10596             });
10597         });
10598         return this;
10599     },
10600
10601    /**
10602     * Animates the transition of an element's dimensions from a starting height/width
10603     * to an ending height/width.
10604     * Usage:
10605 <pre><code>
10606 // change height and width to 100x100 pixels
10607 el.scale(100, 100);
10608
10609 // common config options shown with default values.  The height and width will default to
10610 // the element's existing values if passed as null.
10611 el.scale(
10612     [element's width],
10613     [element's height], {
10614     easing: 'easeOut',
10615     duration: .35
10616 });
10617 </code></pre>
10618     * @param {Number} width  The new width (pass undefined to keep the original width)
10619     * @param {Number} height  The new height (pass undefined to keep the original height)
10620     * @param {Object} options (optional) Object literal with any of the Fx config options
10621     * @return {Roo.Element} The Element
10622     */
10623     scale : function(w, h, o){
10624         this.shift(Roo.apply({}, o, {
10625             width: w,
10626             height: h
10627         }));
10628         return this;
10629     },
10630
10631    /**
10632     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10633     * Any of these properties not specified in the config object will not be changed.  This effect 
10634     * requires that at least one new dimension, position or opacity setting must be passed in on
10635     * the config object in order for the function to have any effect.
10636     * Usage:
10637 <pre><code>
10638 // slide the element horizontally to x position 200 while changing the height and opacity
10639 el.shift({ x: 200, height: 50, opacity: .8 });
10640
10641 // common config options shown with default values.
10642 el.shift({
10643     width: [element's width],
10644     height: [element's height],
10645     x: [element's x position],
10646     y: [element's y position],
10647     opacity: [element's opacity],
10648     easing: 'easeOut',
10649     duration: .35
10650 });
10651 </code></pre>
10652     * @param {Object} options  Object literal with any of the Fx config options
10653     * @return {Roo.Element} The Element
10654     */
10655     shift : function(o){
10656         var el = this.getFxEl();
10657         o = o || {};
10658         el.queueFx(o, function(){
10659             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10660             if(w !== undefined){
10661                 a.width = {to: this.adjustWidth(w)};
10662             }
10663             if(h !== undefined){
10664                 a.height = {to: this.adjustHeight(h)};
10665             }
10666             if(x !== undefined || y !== undefined){
10667                 a.points = {to: [
10668                     x !== undefined ? x : this.getX(),
10669                     y !== undefined ? y : this.getY()
10670                 ]};
10671             }
10672             if(op !== undefined){
10673                 a.opacity = {to: op};
10674             }
10675             if(o.xy !== undefined){
10676                 a.points = {to: o.xy};
10677             }
10678             arguments.callee.anim = this.fxanim(a,
10679                 o, 'motion', .35, "easeOut", function(){
10680                 el.afterFx(o);
10681             });
10682         });
10683         return this;
10684     },
10685
10686         /**
10687          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10688          * ending point of the effect.
10689          * Usage:
10690          *<pre><code>
10691 // default: slide the element downward while fading out
10692 el.ghost();
10693
10694 // custom: slide the element out to the right with a 2-second duration
10695 el.ghost('r', { duration: 2 });
10696
10697 // common config options shown with default values
10698 el.ghost('b', {
10699     easing: 'easeOut',
10700     duration: .5
10701     remove: false,
10702     useDisplay: false
10703 });
10704 </code></pre>
10705          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10706          * @param {Object} options (optional) Object literal with any of the Fx config options
10707          * @return {Roo.Element} The Element
10708          */
10709     ghost : function(anchor, o){
10710         var el = this.getFxEl();
10711         o = o || {};
10712
10713         el.queueFx(o, function(){
10714             anchor = anchor || "b";
10715
10716             // restore values after effect
10717             var r = this.getFxRestore();
10718             var w = this.getWidth(),
10719                 h = this.getHeight();
10720
10721             var st = this.dom.style;
10722
10723             var after = function(){
10724                 if(o.useDisplay){
10725                     el.setDisplayed(false);
10726                 }else{
10727                     el.hide();
10728                 }
10729
10730                 el.clearOpacity();
10731                 el.setPositioning(r.pos);
10732                 st.width = r.width;
10733                 st.height = r.height;
10734
10735                 el.afterFx(o);
10736             };
10737
10738             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10739             switch(anchor.toLowerCase()){
10740                 case "t":
10741                     pt.by = [0, -h];
10742                 break;
10743                 case "l":
10744                     pt.by = [-w, 0];
10745                 break;
10746                 case "r":
10747                     pt.by = [w, 0];
10748                 break;
10749                 case "b":
10750                     pt.by = [0, h];
10751                 break;
10752                 case "tl":
10753                     pt.by = [-w, -h];
10754                 break;
10755                 case "bl":
10756                     pt.by = [-w, h];
10757                 break;
10758                 case "br":
10759                     pt.by = [w, h];
10760                 break;
10761                 case "tr":
10762                     pt.by = [w, -h];
10763                 break;
10764             }
10765
10766             arguments.callee.anim = this.fxanim(a,
10767                 o,
10768                 'motion',
10769                 .5,
10770                 "easeOut", after);
10771         });
10772         return this;
10773     },
10774
10775         /**
10776          * Ensures that all effects queued after syncFx is called on the element are
10777          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10778          * @return {Roo.Element} The Element
10779          */
10780     syncFx : function(){
10781         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10782             block : false,
10783             concurrent : true,
10784             stopFx : false
10785         });
10786         return this;
10787     },
10788
10789         /**
10790          * Ensures that all effects queued after sequenceFx is called on the element are
10791          * run in sequence.  This is the opposite of {@link #syncFx}.
10792          * @return {Roo.Element} The Element
10793          */
10794     sequenceFx : function(){
10795         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10796             block : false,
10797             concurrent : false,
10798             stopFx : false
10799         });
10800         return this;
10801     },
10802
10803         /* @private */
10804     nextFx : function(){
10805         var ef = this.fxQueue[0];
10806         if(ef){
10807             ef.call(this);
10808         }
10809     },
10810
10811         /**
10812          * Returns true if the element has any effects actively running or queued, else returns false.
10813          * @return {Boolean} True if element has active effects, else false
10814          */
10815     hasActiveFx : function(){
10816         return this.fxQueue && this.fxQueue[0];
10817     },
10818
10819         /**
10820          * Stops any running effects and clears the element's internal effects queue if it contains
10821          * any additional effects that haven't started yet.
10822          * @return {Roo.Element} The Element
10823          */
10824     stopFx : function(){
10825         if(this.hasActiveFx()){
10826             var cur = this.fxQueue[0];
10827             if(cur && cur.anim && cur.anim.isAnimated()){
10828                 this.fxQueue = [cur]; // clear out others
10829                 cur.anim.stop(true);
10830             }
10831         }
10832         return this;
10833     },
10834
10835         /* @private */
10836     beforeFx : function(o){
10837         if(this.hasActiveFx() && !o.concurrent){
10838            if(o.stopFx){
10839                this.stopFx();
10840                return true;
10841            }
10842            return false;
10843         }
10844         return true;
10845     },
10846
10847         /**
10848          * Returns true if the element is currently blocking so that no other effect can be queued
10849          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10850          * used to ensure that an effect initiated by a user action runs to completion prior to the
10851          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10852          * @return {Boolean} True if blocking, else false
10853          */
10854     hasFxBlock : function(){
10855         var q = this.fxQueue;
10856         return q && q[0] && q[0].block;
10857     },
10858
10859         /* @private */
10860     queueFx : function(o, fn){
10861         if(!this.fxQueue){
10862             this.fxQueue = [];
10863         }
10864         if(!this.hasFxBlock()){
10865             Roo.applyIf(o, this.fxDefaults);
10866             if(!o.concurrent){
10867                 var run = this.beforeFx(o);
10868                 fn.block = o.block;
10869                 this.fxQueue.push(fn);
10870                 if(run){
10871                     this.nextFx();
10872                 }
10873             }else{
10874                 fn.call(this);
10875             }
10876         }
10877         return this;
10878     },
10879
10880         /* @private */
10881     fxWrap : function(pos, o, vis){
10882         var wrap;
10883         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10884             var wrapXY;
10885             if(o.fixPosition){
10886                 wrapXY = this.getXY();
10887             }
10888             var div = document.createElement("div");
10889             div.style.visibility = vis;
10890             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10891             wrap.setPositioning(pos);
10892             if(wrap.getStyle("position") == "static"){
10893                 wrap.position("relative");
10894             }
10895             this.clearPositioning('auto');
10896             wrap.clip();
10897             wrap.dom.appendChild(this.dom);
10898             if(wrapXY){
10899                 wrap.setXY(wrapXY);
10900             }
10901         }
10902         return wrap;
10903     },
10904
10905         /* @private */
10906     fxUnwrap : function(wrap, pos, o){
10907         this.clearPositioning();
10908         this.setPositioning(pos);
10909         if(!o.wrap){
10910             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10911             wrap.remove();
10912         }
10913     },
10914
10915         /* @private */
10916     getFxRestore : function(){
10917         var st = this.dom.style;
10918         return {pos: this.getPositioning(), width: st.width, height : st.height};
10919     },
10920
10921         /* @private */
10922     afterFx : function(o){
10923         if(o.afterStyle){
10924             this.applyStyles(o.afterStyle);
10925         }
10926         if(o.afterCls){
10927             this.addClass(o.afterCls);
10928         }
10929         if(o.remove === true){
10930             this.remove();
10931         }
10932         Roo.callback(o.callback, o.scope, [this]);
10933         if(!o.concurrent){
10934             this.fxQueue.shift();
10935             this.nextFx();
10936         }
10937     },
10938
10939         /* @private */
10940     getFxEl : function(){ // support for composite element fx
10941         return Roo.get(this.dom);
10942     },
10943
10944         /* @private */
10945     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10946         animType = animType || 'run';
10947         opt = opt || {};
10948         var anim = Roo.lib.Anim[animType](
10949             this.dom, args,
10950             (opt.duration || defaultDur) || .35,
10951             (opt.easing || defaultEase) || 'easeOut',
10952             function(){
10953                 Roo.callback(cb, this);
10954             },
10955             this
10956         );
10957         opt.anim = anim;
10958         return anim;
10959     }
10960 };
10961
10962 // backwords compat
10963 Roo.Fx.resize = Roo.Fx.scale;
10964
10965 //When included, Roo.Fx is automatically applied to Element so that all basic
10966 //effects are available directly via the Element API
10967 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10968  * Based on:
10969  * Ext JS Library 1.1.1
10970  * Copyright(c) 2006-2007, Ext JS, LLC.
10971  *
10972  * Originally Released Under LGPL - original licence link has changed is not relivant.
10973  *
10974  * Fork - LGPL
10975  * <script type="text/javascript">
10976  */
10977
10978
10979 /**
10980  * @class Roo.CompositeElement
10981  * Standard composite class. Creates a Roo.Element for every element in the collection.
10982  * <br><br>
10983  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10984  * actions will be performed on all the elements in this collection.</b>
10985  * <br><br>
10986  * All methods return <i>this</i> and can be chained.
10987  <pre><code>
10988  var els = Roo.select("#some-el div.some-class", true);
10989  // or select directly from an existing element
10990  var el = Roo.get('some-el');
10991  el.select('div.some-class', true);
10992
10993  els.setWidth(100); // all elements become 100 width
10994  els.hide(true); // all elements fade out and hide
10995  // or
10996  els.setWidth(100).hide(true);
10997  </code></pre>
10998  */
10999 Roo.CompositeElement = function(els){
11000     this.elements = [];
11001     this.addElements(els);
11002 };
11003 Roo.CompositeElement.prototype = {
11004     isComposite: true,
11005     addElements : function(els){
11006         if(!els) {
11007             return this;
11008         }
11009         if(typeof els == "string"){
11010             els = Roo.Element.selectorFunction(els);
11011         }
11012         var yels = this.elements;
11013         var index = yels.length-1;
11014         for(var i = 0, len = els.length; i < len; i++) {
11015                 yels[++index] = Roo.get(els[i]);
11016         }
11017         return this;
11018     },
11019
11020     /**
11021     * Clears this composite and adds the elements returned by the passed selector.
11022     * @param {String/Array} els A string CSS selector, an array of elements or an element
11023     * @return {CompositeElement} this
11024     */
11025     fill : function(els){
11026         this.elements = [];
11027         this.add(els);
11028         return this;
11029     },
11030
11031     /**
11032     * Filters this composite to only elements that match the passed selector.
11033     * @param {String} selector A string CSS selector
11034     * @param {Boolean} inverse return inverse filter (not matches)
11035     * @return {CompositeElement} this
11036     */
11037     filter : function(selector, inverse){
11038         var els = [];
11039         inverse = inverse || false;
11040         this.each(function(el){
11041             var match = inverse ? !el.is(selector) : el.is(selector);
11042             if(match){
11043                 els[els.length] = el.dom;
11044             }
11045         });
11046         this.fill(els);
11047         return this;
11048     },
11049
11050     invoke : function(fn, args){
11051         var els = this.elements;
11052         for(var i = 0, len = els.length; i < len; i++) {
11053                 Roo.Element.prototype[fn].apply(els[i], args);
11054         }
11055         return this;
11056     },
11057     /**
11058     * Adds elements to this composite.
11059     * @param {String/Array} els A string CSS selector, an array of elements or an element
11060     * @return {CompositeElement} this
11061     */
11062     add : function(els){
11063         if(typeof els == "string"){
11064             this.addElements(Roo.Element.selectorFunction(els));
11065         }else if(els.length !== undefined){
11066             this.addElements(els);
11067         }else{
11068             this.addElements([els]);
11069         }
11070         return this;
11071     },
11072     /**
11073     * Calls the passed function passing (el, this, index) for each element in this composite.
11074     * @param {Function} fn The function to call
11075     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11076     * @return {CompositeElement} this
11077     */
11078     each : function(fn, scope){
11079         var els = this.elements;
11080         for(var i = 0, len = els.length; i < len; i++){
11081             if(fn.call(scope || els[i], els[i], this, i) === false) {
11082                 break;
11083             }
11084         }
11085         return this;
11086     },
11087
11088     /**
11089      * Returns the Element object at the specified index
11090      * @param {Number} index
11091      * @return {Roo.Element}
11092      */
11093     item : function(index){
11094         return this.elements[index] || null;
11095     },
11096
11097     /**
11098      * Returns the first Element
11099      * @return {Roo.Element}
11100      */
11101     first : function(){
11102         return this.item(0);
11103     },
11104
11105     /**
11106      * Returns the last Element
11107      * @return {Roo.Element}
11108      */
11109     last : function(){
11110         return this.item(this.elements.length-1);
11111     },
11112
11113     /**
11114      * Returns the number of elements in this composite
11115      * @return Number
11116      */
11117     getCount : function(){
11118         return this.elements.length;
11119     },
11120
11121     /**
11122      * Returns true if this composite contains the passed element
11123      * @return Boolean
11124      */
11125     contains : function(el){
11126         return this.indexOf(el) !== -1;
11127     },
11128
11129     /**
11130      * Returns true if this composite contains the passed element
11131      * @return Boolean
11132      */
11133     indexOf : function(el){
11134         return this.elements.indexOf(Roo.get(el));
11135     },
11136
11137
11138     /**
11139     * Removes the specified element(s).
11140     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11141     * or an array of any of those.
11142     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11143     * @return {CompositeElement} this
11144     */
11145     removeElement : function(el, removeDom){
11146         if(el instanceof Array){
11147             for(var i = 0, len = el.length; i < len; i++){
11148                 this.removeElement(el[i]);
11149             }
11150             return this;
11151         }
11152         var index = typeof el == 'number' ? el : this.indexOf(el);
11153         if(index !== -1){
11154             if(removeDom){
11155                 var d = this.elements[index];
11156                 if(d.dom){
11157                     d.remove();
11158                 }else{
11159                     d.parentNode.removeChild(d);
11160                 }
11161             }
11162             this.elements.splice(index, 1);
11163         }
11164         return this;
11165     },
11166
11167     /**
11168     * Replaces the specified element with the passed element.
11169     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11170     * to replace.
11171     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11172     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11173     * @return {CompositeElement} this
11174     */
11175     replaceElement : function(el, replacement, domReplace){
11176         var index = typeof el == 'number' ? el : this.indexOf(el);
11177         if(index !== -1){
11178             if(domReplace){
11179                 this.elements[index].replaceWith(replacement);
11180             }else{
11181                 this.elements.splice(index, 1, Roo.get(replacement))
11182             }
11183         }
11184         return this;
11185     },
11186
11187     /**
11188      * Removes all elements.
11189      */
11190     clear : function(){
11191         this.elements = [];
11192     }
11193 };
11194 (function(){
11195     Roo.CompositeElement.createCall = function(proto, fnName){
11196         if(!proto[fnName]){
11197             proto[fnName] = function(){
11198                 return this.invoke(fnName, arguments);
11199             };
11200         }
11201     };
11202     for(var fnName in Roo.Element.prototype){
11203         if(typeof Roo.Element.prototype[fnName] == "function"){
11204             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11205         }
11206     };
11207 })();
11208 /*
11209  * Based on:
11210  * Ext JS Library 1.1.1
11211  * Copyright(c) 2006-2007, Ext JS, LLC.
11212  *
11213  * Originally Released Under LGPL - original licence link has changed is not relivant.
11214  *
11215  * Fork - LGPL
11216  * <script type="text/javascript">
11217  */
11218
11219 /**
11220  * @class Roo.CompositeElementLite
11221  * @extends Roo.CompositeElement
11222  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11223  <pre><code>
11224  var els = Roo.select("#some-el div.some-class");
11225  // or select directly from an existing element
11226  var el = Roo.get('some-el');
11227  el.select('div.some-class');
11228
11229  els.setWidth(100); // all elements become 100 width
11230  els.hide(true); // all elements fade out and hide
11231  // or
11232  els.setWidth(100).hide(true);
11233  </code></pre><br><br>
11234  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11235  * actions will be performed on all the elements in this collection.</b>
11236  */
11237 Roo.CompositeElementLite = function(els){
11238     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11239     this.el = new Roo.Element.Flyweight();
11240 };
11241 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11242     addElements : function(els){
11243         if(els){
11244             if(els instanceof Array){
11245                 this.elements = this.elements.concat(els);
11246             }else{
11247                 var yels = this.elements;
11248                 var index = yels.length-1;
11249                 for(var i = 0, len = els.length; i < len; i++) {
11250                     yels[++index] = els[i];
11251                 }
11252             }
11253         }
11254         return this;
11255     },
11256     invoke : function(fn, args){
11257         var els = this.elements;
11258         var el = this.el;
11259         for(var i = 0, len = els.length; i < len; i++) {
11260             el.dom = els[i];
11261                 Roo.Element.prototype[fn].apply(el, args);
11262         }
11263         return this;
11264     },
11265     /**
11266      * Returns a flyweight Element of the dom element object at the specified index
11267      * @param {Number} index
11268      * @return {Roo.Element}
11269      */
11270     item : function(index){
11271         if(!this.elements[index]){
11272             return null;
11273         }
11274         this.el.dom = this.elements[index];
11275         return this.el;
11276     },
11277
11278     // fixes scope with flyweight
11279     addListener : function(eventName, handler, scope, opt){
11280         var els = this.elements;
11281         for(var i = 0, len = els.length; i < len; i++) {
11282             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11283         }
11284         return this;
11285     },
11286
11287     /**
11288     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11289     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11290     * a reference to the dom node, use el.dom.</b>
11291     * @param {Function} fn The function to call
11292     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11293     * @return {CompositeElement} this
11294     */
11295     each : function(fn, scope){
11296         var els = this.elements;
11297         var el = this.el;
11298         for(var i = 0, len = els.length; i < len; i++){
11299             el.dom = els[i];
11300                 if(fn.call(scope || el, el, this, i) === false){
11301                 break;
11302             }
11303         }
11304         return this;
11305     },
11306
11307     indexOf : function(el){
11308         return this.elements.indexOf(Roo.getDom(el));
11309     },
11310
11311     replaceElement : function(el, replacement, domReplace){
11312         var index = typeof el == 'number' ? el : this.indexOf(el);
11313         if(index !== -1){
11314             replacement = Roo.getDom(replacement);
11315             if(domReplace){
11316                 var d = this.elements[index];
11317                 d.parentNode.insertBefore(replacement, d);
11318                 d.parentNode.removeChild(d);
11319             }
11320             this.elements.splice(index, 1, replacement);
11321         }
11322         return this;
11323     }
11324 });
11325 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11326
11327 /*
11328  * Based on:
11329  * Ext JS Library 1.1.1
11330  * Copyright(c) 2006-2007, Ext JS, LLC.
11331  *
11332  * Originally Released Under LGPL - original licence link has changed is not relivant.
11333  *
11334  * Fork - LGPL
11335  * <script type="text/javascript">
11336  */
11337
11338  
11339
11340 /**
11341  * @class Roo.data.Connection
11342  * @extends Roo.util.Observable
11343  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11344  * either to a configured URL, or to a URL specified at request time.<br><br>
11345  * <p>
11346  * Requests made by this class are asynchronous, and will return immediately. No data from
11347  * the server will be available to the statement immediately following the {@link #request} call.
11348  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11349  * <p>
11350  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11351  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11352  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11353  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11354  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11355  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11356  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11357  * standard DOM methods.
11358  * @constructor
11359  * @param {Object} config a configuration object.
11360  */
11361 Roo.data.Connection = function(config){
11362     Roo.apply(this, config);
11363     this.addEvents({
11364         /**
11365          * @event beforerequest
11366          * Fires before a network request is made to retrieve a data object.
11367          * @param {Connection} conn This Connection object.
11368          * @param {Object} options The options config object passed to the {@link #request} method.
11369          */
11370         "beforerequest" : true,
11371         /**
11372          * @event requestcomplete
11373          * Fires if the request was successfully completed.
11374          * @param {Connection} conn This Connection object.
11375          * @param {Object} response The XHR object containing the response data.
11376          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11377          * @param {Object} options The options config object passed to the {@link #request} method.
11378          */
11379         "requestcomplete" : true,
11380         /**
11381          * @event requestexception
11382          * Fires if an error HTTP status was returned from the server.
11383          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11384          * @param {Connection} conn This Connection object.
11385          * @param {Object} response The XHR object containing the response data.
11386          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11387          * @param {Object} options The options config object passed to the {@link #request} method.
11388          */
11389         "requestexception" : true
11390     });
11391     Roo.data.Connection.superclass.constructor.call(this);
11392 };
11393
11394 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11395     /**
11396      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11397      */
11398     /**
11399      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11400      * extra parameters to each request made by this object. (defaults to undefined)
11401      */
11402     /**
11403      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11404      *  to each request made by this object. (defaults to undefined)
11405      */
11406     /**
11407      * @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)
11408      */
11409     /**
11410      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11411      */
11412     timeout : 30000,
11413     /**
11414      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11415      * @type Boolean
11416      */
11417     autoAbort:false,
11418
11419     /**
11420      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11421      * @type Boolean
11422      */
11423     disableCaching: true,
11424
11425     /**
11426      * Sends an HTTP request to a remote server.
11427      * @param {Object} options An object which may contain the following properties:<ul>
11428      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11429      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11430      * request, a url encoded string or a function to call to get either.</li>
11431      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11432      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11433      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11434      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11435      * <li>options {Object} The parameter to the request call.</li>
11436      * <li>success {Boolean} True if the request succeeded.</li>
11437      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11438      * </ul></li>
11439      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11440      * The callback is passed the following parameters:<ul>
11441      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11442      * <li>options {Object} The parameter to the request call.</li>
11443      * </ul></li>
11444      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11445      * The callback is passed the following parameters:<ul>
11446      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11447      * <li>options {Object} The parameter to the request call.</li>
11448      * </ul></li>
11449      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11450      * for the callback function. Defaults to the browser window.</li>
11451      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11452      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11453      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11454      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11455      * params for the post data. Any params will be appended to the URL.</li>
11456      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11457      * </ul>
11458      * @return {Number} transactionId
11459      */
11460     request : function(o){
11461         if(this.fireEvent("beforerequest", this, o) !== false){
11462             var p = o.params;
11463
11464             if(typeof p == "function"){
11465                 p = p.call(o.scope||window, o);
11466             }
11467             if(typeof p == "object"){
11468                 p = Roo.urlEncode(o.params);
11469             }
11470             if(this.extraParams){
11471                 var extras = Roo.urlEncode(this.extraParams);
11472                 p = p ? (p + '&' + extras) : extras;
11473             }
11474
11475             var url = o.url || this.url;
11476             if(typeof url == 'function'){
11477                 url = url.call(o.scope||window, o);
11478             }
11479
11480             if(o.form){
11481                 var form = Roo.getDom(o.form);
11482                 url = url || form.action;
11483
11484                 var enctype = form.getAttribute("enctype");
11485                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11486                     return this.doFormUpload(o, p, url);
11487                 }
11488                 var f = Roo.lib.Ajax.serializeForm(form);
11489                 p = p ? (p + '&' + f) : f;
11490             }
11491
11492             var hs = o.headers;
11493             if(this.defaultHeaders){
11494                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11495                 if(!o.headers){
11496                     o.headers = hs;
11497                 }
11498             }
11499
11500             var cb = {
11501                 success: this.handleResponse,
11502                 failure: this.handleFailure,
11503                 scope: this,
11504                 argument: {options: o},
11505                 timeout : o.timeout || this.timeout
11506             };
11507
11508             var method = o.method||this.method||(p ? "POST" : "GET");
11509
11510             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11511                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11512             }
11513
11514             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11515                 if(o.autoAbort){
11516                     this.abort();
11517                 }
11518             }else if(this.autoAbort !== false){
11519                 this.abort();
11520             }
11521
11522             if((method == 'GET' && p) || o.xmlData){
11523                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11524                 p = '';
11525             }
11526             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11527             return this.transId;
11528         }else{
11529             Roo.callback(o.callback, o.scope, [o, null, null]);
11530             return null;
11531         }
11532     },
11533
11534     /**
11535      * Determine whether this object has a request outstanding.
11536      * @param {Number} transactionId (Optional) defaults to the last transaction
11537      * @return {Boolean} True if there is an outstanding request.
11538      */
11539     isLoading : function(transId){
11540         if(transId){
11541             return Roo.lib.Ajax.isCallInProgress(transId);
11542         }else{
11543             return this.transId ? true : false;
11544         }
11545     },
11546
11547     /**
11548      * Aborts any outstanding request.
11549      * @param {Number} transactionId (Optional) defaults to the last transaction
11550      */
11551     abort : function(transId){
11552         if(transId || this.isLoading()){
11553             Roo.lib.Ajax.abort(transId || this.transId);
11554         }
11555     },
11556
11557     // private
11558     handleResponse : function(response){
11559         this.transId = false;
11560         var options = response.argument.options;
11561         response.argument = options ? options.argument : null;
11562         this.fireEvent("requestcomplete", this, response, options);
11563         Roo.callback(options.success, options.scope, [response, options]);
11564         Roo.callback(options.callback, options.scope, [options, true, response]);
11565     },
11566
11567     // private
11568     handleFailure : function(response, e){
11569         this.transId = false;
11570         var options = response.argument.options;
11571         response.argument = options ? options.argument : null;
11572         this.fireEvent("requestexception", this, response, options, e);
11573         Roo.callback(options.failure, options.scope, [response, options]);
11574         Roo.callback(options.callback, options.scope, [options, false, response]);
11575     },
11576
11577     // private
11578     doFormUpload : function(o, ps, url){
11579         var id = Roo.id();
11580         var frame = document.createElement('iframe');
11581         frame.id = id;
11582         frame.name = id;
11583         frame.className = 'x-hidden';
11584         if(Roo.isIE){
11585             frame.src = Roo.SSL_SECURE_URL;
11586         }
11587         document.body.appendChild(frame);
11588
11589         if(Roo.isIE){
11590            document.frames[id].name = id;
11591         }
11592
11593         var form = Roo.getDom(o.form);
11594         form.target = id;
11595         form.method = 'POST';
11596         form.enctype = form.encoding = 'multipart/form-data';
11597         if(url){
11598             form.action = url;
11599         }
11600
11601         var hiddens, hd;
11602         if(ps){ // add dynamic params
11603             hiddens = [];
11604             ps = Roo.urlDecode(ps, false);
11605             for(var k in ps){
11606                 if(ps.hasOwnProperty(k)){
11607                     hd = document.createElement('input');
11608                     hd.type = 'hidden';
11609                     hd.name = k;
11610                     hd.value = ps[k];
11611                     form.appendChild(hd);
11612                     hiddens.push(hd);
11613                 }
11614             }
11615         }
11616
11617         function cb(){
11618             var r = {  // bogus response object
11619                 responseText : '',
11620                 responseXML : null
11621             };
11622
11623             r.argument = o ? o.argument : null;
11624
11625             try { //
11626                 var doc;
11627                 if(Roo.isIE){
11628                     doc = frame.contentWindow.document;
11629                 }else {
11630                     doc = (frame.contentDocument || window.frames[id].document);
11631                 }
11632                 if(doc && doc.body){
11633                     r.responseText = doc.body.innerHTML;
11634                 }
11635                 if(doc && doc.XMLDocument){
11636                     r.responseXML = doc.XMLDocument;
11637                 }else {
11638                     r.responseXML = doc;
11639                 }
11640             }
11641             catch(e) {
11642                 // ignore
11643             }
11644
11645             Roo.EventManager.removeListener(frame, 'load', cb, this);
11646
11647             this.fireEvent("requestcomplete", this, r, o);
11648             Roo.callback(o.success, o.scope, [r, o]);
11649             Roo.callback(o.callback, o.scope, [o, true, r]);
11650
11651             setTimeout(function(){document.body.removeChild(frame);}, 100);
11652         }
11653
11654         Roo.EventManager.on(frame, 'load', cb, this);
11655         form.submit();
11656
11657         if(hiddens){ // remove dynamic params
11658             for(var i = 0, len = hiddens.length; i < len; i++){
11659                 form.removeChild(hiddens[i]);
11660             }
11661         }
11662     }
11663 });
11664 /*
11665  * Based on:
11666  * Ext JS Library 1.1.1
11667  * Copyright(c) 2006-2007, Ext JS, LLC.
11668  *
11669  * Originally Released Under LGPL - original licence link has changed is not relivant.
11670  *
11671  * Fork - LGPL
11672  * <script type="text/javascript">
11673  */
11674  
11675 /**
11676  * Global Ajax request class.
11677  * 
11678  * @class Roo.Ajax
11679  * @extends Roo.data.Connection
11680  * @static
11681  * 
11682  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11683  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11684  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11685  * @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)
11686  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11687  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11688  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11689  */
11690 Roo.Ajax = new Roo.data.Connection({
11691     // fix up the docs
11692     /**
11693      * @scope Roo.Ajax
11694      * @type {Boolear} 
11695      */
11696     autoAbort : false,
11697
11698     /**
11699      * Serialize the passed form into a url encoded string
11700      * @scope Roo.Ajax
11701      * @param {String/HTMLElement} form
11702      * @return {String}
11703      */
11704     serializeForm : function(form){
11705         return Roo.lib.Ajax.serializeForm(form);
11706     }
11707 });/*
11708  * Based on:
11709  * Ext JS Library 1.1.1
11710  * Copyright(c) 2006-2007, Ext JS, LLC.
11711  *
11712  * Originally Released Under LGPL - original licence link has changed is not relivant.
11713  *
11714  * Fork - LGPL
11715  * <script type="text/javascript">
11716  */
11717
11718  
11719 /**
11720  * @class Roo.UpdateManager
11721  * @extends Roo.util.Observable
11722  * Provides AJAX-style update for Element object.<br><br>
11723  * Usage:<br>
11724  * <pre><code>
11725  * // Get it from a Roo.Element object
11726  * var el = Roo.get("foo");
11727  * var mgr = el.getUpdateManager();
11728  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11729  * ...
11730  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11731  * <br>
11732  * // or directly (returns the same UpdateManager instance)
11733  * var mgr = new Roo.UpdateManager("myElementId");
11734  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11735  * mgr.on("update", myFcnNeedsToKnow);
11736  * <br>
11737    // short handed call directly from the element object
11738    Roo.get("foo").load({
11739         url: "bar.php",
11740         scripts:true,
11741         params: "for=bar",
11742         text: "Loading Foo..."
11743    });
11744  * </code></pre>
11745  * @constructor
11746  * Create new UpdateManager directly.
11747  * @param {String/HTMLElement/Roo.Element} el The element to update
11748  * @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).
11749  */
11750 Roo.UpdateManager = function(el, forceNew){
11751     el = Roo.get(el);
11752     if(!forceNew && el.updateManager){
11753         return el.updateManager;
11754     }
11755     /**
11756      * The Element object
11757      * @type Roo.Element
11758      */
11759     this.el = el;
11760     /**
11761      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11762      * @type String
11763      */
11764     this.defaultUrl = null;
11765
11766     this.addEvents({
11767         /**
11768          * @event beforeupdate
11769          * Fired before an update is made, return false from your handler and the update is cancelled.
11770          * @param {Roo.Element} el
11771          * @param {String/Object/Function} url
11772          * @param {String/Object} params
11773          */
11774         "beforeupdate": true,
11775         /**
11776          * @event update
11777          * Fired after successful update is made.
11778          * @param {Roo.Element} el
11779          * @param {Object} oResponseObject The response Object
11780          */
11781         "update": true,
11782         /**
11783          * @event failure
11784          * Fired on update failure.
11785          * @param {Roo.Element} el
11786          * @param {Object} oResponseObject The response Object
11787          */
11788         "failure": true
11789     });
11790     var d = Roo.UpdateManager.defaults;
11791     /**
11792      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11793      * @type String
11794      */
11795     this.sslBlankUrl = d.sslBlankUrl;
11796     /**
11797      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11798      * @type Boolean
11799      */
11800     this.disableCaching = d.disableCaching;
11801     /**
11802      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11803      * @type String
11804      */
11805     this.indicatorText = d.indicatorText;
11806     /**
11807      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11808      * @type String
11809      */
11810     this.showLoadIndicator = d.showLoadIndicator;
11811     /**
11812      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11813      * @type Number
11814      */
11815     this.timeout = d.timeout;
11816
11817     /**
11818      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11819      * @type Boolean
11820      */
11821     this.loadScripts = d.loadScripts;
11822
11823     /**
11824      * Transaction object of current executing transaction
11825      */
11826     this.transaction = null;
11827
11828     /**
11829      * @private
11830      */
11831     this.autoRefreshProcId = null;
11832     /**
11833      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11834      * @type Function
11835      */
11836     this.refreshDelegate = this.refresh.createDelegate(this);
11837     /**
11838      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11839      * @type Function
11840      */
11841     this.updateDelegate = this.update.createDelegate(this);
11842     /**
11843      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11844      * @type Function
11845      */
11846     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11847     /**
11848      * @private
11849      */
11850     this.successDelegate = this.processSuccess.createDelegate(this);
11851     /**
11852      * @private
11853      */
11854     this.failureDelegate = this.processFailure.createDelegate(this);
11855
11856     if(!this.renderer){
11857      /**
11858       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11859       */
11860     this.renderer = new Roo.UpdateManager.BasicRenderer();
11861     }
11862     
11863     Roo.UpdateManager.superclass.constructor.call(this);
11864 };
11865
11866 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11867     /**
11868      * Get the Element this UpdateManager is bound to
11869      * @return {Roo.Element} The element
11870      */
11871     getEl : function(){
11872         return this.el;
11873     },
11874     /**
11875      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11876      * @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:
11877 <pre><code>
11878 um.update({<br/>
11879     url: "your-url.php",<br/>
11880     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11881     callback: yourFunction,<br/>
11882     scope: yourObject, //(optional scope)  <br/>
11883     discardUrl: false, <br/>
11884     nocache: false,<br/>
11885     text: "Loading...",<br/>
11886     timeout: 30,<br/>
11887     scripts: false<br/>
11888 });
11889 </code></pre>
11890      * The only required property is url. The optional properties nocache, text and scripts
11891      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11892      * @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}
11893      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11894      * @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.
11895      */
11896     update : function(url, params, callback, discardUrl){
11897         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11898             var method = this.method,
11899                 cfg;
11900             if(typeof url == "object"){ // must be config object
11901                 cfg = url;
11902                 url = cfg.url;
11903                 params = params || cfg.params;
11904                 callback = callback || cfg.callback;
11905                 discardUrl = discardUrl || cfg.discardUrl;
11906                 if(callback && cfg.scope){
11907                     callback = callback.createDelegate(cfg.scope);
11908                 }
11909                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11910                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11911                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11912                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11913                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11914             }
11915             this.showLoading();
11916             if(!discardUrl){
11917                 this.defaultUrl = url;
11918             }
11919             if(typeof url == "function"){
11920                 url = url.call(this);
11921             }
11922
11923             method = method || (params ? "POST" : "GET");
11924             if(method == "GET"){
11925                 url = this.prepareUrl(url);
11926             }
11927
11928             var o = Roo.apply(cfg ||{}, {
11929                 url : url,
11930                 params: params,
11931                 success: this.successDelegate,
11932                 failure: this.failureDelegate,
11933                 callback: undefined,
11934                 timeout: (this.timeout*1000),
11935                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11936             });
11937             Roo.log("updated manager called with timeout of " + o.timeout);
11938             this.transaction = Roo.Ajax.request(o);
11939         }
11940     },
11941
11942     /**
11943      * 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.
11944      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11945      * @param {String/HTMLElement} form The form Id or form element
11946      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11947      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11948      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11949      */
11950     formUpdate : function(form, url, reset, callback){
11951         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11952             if(typeof url == "function"){
11953                 url = url.call(this);
11954             }
11955             form = Roo.getDom(form);
11956             this.transaction = Roo.Ajax.request({
11957                 form: form,
11958                 url:url,
11959                 success: this.successDelegate,
11960                 failure: this.failureDelegate,
11961                 timeout: (this.timeout*1000),
11962                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11963             });
11964             this.showLoading.defer(1, this);
11965         }
11966     },
11967
11968     /**
11969      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11970      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11971      */
11972     refresh : function(callback){
11973         if(this.defaultUrl == null){
11974             return;
11975         }
11976         this.update(this.defaultUrl, null, callback, true);
11977     },
11978
11979     /**
11980      * Set this element to auto refresh.
11981      * @param {Number} interval How often to update (in seconds).
11982      * @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)
11983      * @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}
11984      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11985      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11986      */
11987     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11988         if(refreshNow){
11989             this.update(url || this.defaultUrl, params, callback, true);
11990         }
11991         if(this.autoRefreshProcId){
11992             clearInterval(this.autoRefreshProcId);
11993         }
11994         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11995     },
11996
11997     /**
11998      * Stop auto refresh on this element.
11999      */
12000      stopAutoRefresh : function(){
12001         if(this.autoRefreshProcId){
12002             clearInterval(this.autoRefreshProcId);
12003             delete this.autoRefreshProcId;
12004         }
12005     },
12006
12007     isAutoRefreshing : function(){
12008        return this.autoRefreshProcId ? true : false;
12009     },
12010     /**
12011      * Called to update the element to "Loading" state. Override to perform custom action.
12012      */
12013     showLoading : function(){
12014         if(this.showLoadIndicator){
12015             this.el.update(this.indicatorText);
12016         }
12017     },
12018
12019     /**
12020      * Adds unique parameter to query string if disableCaching = true
12021      * @private
12022      */
12023     prepareUrl : function(url){
12024         if(this.disableCaching){
12025             var append = "_dc=" + (new Date().getTime());
12026             if(url.indexOf("?") !== -1){
12027                 url += "&" + append;
12028             }else{
12029                 url += "?" + append;
12030             }
12031         }
12032         return url;
12033     },
12034
12035     /**
12036      * @private
12037      */
12038     processSuccess : function(response){
12039         this.transaction = null;
12040         if(response.argument.form && response.argument.reset){
12041             try{ // put in try/catch since some older FF releases had problems with this
12042                 response.argument.form.reset();
12043             }catch(e){}
12044         }
12045         if(this.loadScripts){
12046             this.renderer.render(this.el, response, this,
12047                 this.updateComplete.createDelegate(this, [response]));
12048         }else{
12049             this.renderer.render(this.el, response, this);
12050             this.updateComplete(response);
12051         }
12052     },
12053
12054     updateComplete : function(response){
12055         this.fireEvent("update", this.el, response);
12056         if(typeof response.argument.callback == "function"){
12057             response.argument.callback(this.el, true, response);
12058         }
12059     },
12060
12061     /**
12062      * @private
12063      */
12064     processFailure : function(response){
12065         this.transaction = null;
12066         this.fireEvent("failure", this.el, response);
12067         if(typeof response.argument.callback == "function"){
12068             response.argument.callback(this.el, false, response);
12069         }
12070     },
12071
12072     /**
12073      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12074      * @param {Object} renderer The object implementing the render() method
12075      */
12076     setRenderer : function(renderer){
12077         this.renderer = renderer;
12078     },
12079
12080     getRenderer : function(){
12081        return this.renderer;
12082     },
12083
12084     /**
12085      * Set the defaultUrl used for updates
12086      * @param {String/Function} defaultUrl The url or a function to call to get the url
12087      */
12088     setDefaultUrl : function(defaultUrl){
12089         this.defaultUrl = defaultUrl;
12090     },
12091
12092     /**
12093      * Aborts the executing transaction
12094      */
12095     abort : function(){
12096         if(this.transaction){
12097             Roo.Ajax.abort(this.transaction);
12098         }
12099     },
12100
12101     /**
12102      * Returns true if an update is in progress
12103      * @return {Boolean}
12104      */
12105     isUpdating : function(){
12106         if(this.transaction){
12107             return Roo.Ajax.isLoading(this.transaction);
12108         }
12109         return false;
12110     }
12111 });
12112
12113 /**
12114  * @class Roo.UpdateManager.defaults
12115  * @static (not really - but it helps the doc tool)
12116  * The defaults collection enables customizing the default properties of UpdateManager
12117  */
12118    Roo.UpdateManager.defaults = {
12119        /**
12120          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12121          * @type Number
12122          */
12123          timeout : 30,
12124
12125          /**
12126          * True to process scripts by default (Defaults to false).
12127          * @type Boolean
12128          */
12129         loadScripts : false,
12130
12131         /**
12132         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12133         * @type String
12134         */
12135         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12136         /**
12137          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12138          * @type Boolean
12139          */
12140         disableCaching : false,
12141         /**
12142          * Whether to show indicatorText when loading (Defaults to true).
12143          * @type Boolean
12144          */
12145         showLoadIndicator : true,
12146         /**
12147          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12148          * @type String
12149          */
12150         indicatorText : '<div class="loading-indicator">Loading...</div>'
12151    };
12152
12153 /**
12154  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12155  *Usage:
12156  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12157  * @param {String/HTMLElement/Roo.Element} el The element to update
12158  * @param {String} url The url
12159  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12160  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12161  * @static
12162  * @deprecated
12163  * @member Roo.UpdateManager
12164  */
12165 Roo.UpdateManager.updateElement = function(el, url, params, options){
12166     var um = Roo.get(el, true).getUpdateManager();
12167     Roo.apply(um, options);
12168     um.update(url, params, options ? options.callback : null);
12169 };
12170 // alias for backwards compat
12171 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12172 /**
12173  * @class Roo.UpdateManager.BasicRenderer
12174  * Default Content renderer. Updates the elements innerHTML with the responseText.
12175  */
12176 Roo.UpdateManager.BasicRenderer = function(){};
12177
12178 Roo.UpdateManager.BasicRenderer.prototype = {
12179     /**
12180      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12181      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12182      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12183      * @param {Roo.Element} el The element being rendered
12184      * @param {Object} response The YUI Connect response object
12185      * @param {UpdateManager} updateManager The calling update manager
12186      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12187      */
12188      render : function(el, response, updateManager, callback){
12189         el.update(response.responseText, updateManager.loadScripts, callback);
12190     }
12191 };
12192 /*
12193  * Based on:
12194  * Roo JS
12195  * (c)) Alan Knowles
12196  * Licence : LGPL
12197  */
12198
12199
12200 /**
12201  * @class Roo.DomTemplate
12202  * @extends Roo.Template
12203  * An effort at a dom based template engine..
12204  *
12205  * Similar to XTemplate, except it uses dom parsing to create the template..
12206  *
12207  * Supported features:
12208  *
12209  *  Tags:
12210
12211 <pre><code>
12212       {a_variable} - output encoded.
12213       {a_variable.format:("Y-m-d")} - call a method on the variable
12214       {a_variable:raw} - unencoded output
12215       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12216       {a_variable:this.method_on_template(...)} - call a method on the template object.
12217  
12218 </code></pre>
12219  *  The tpl tag:
12220 <pre><code>
12221         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12222         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12223         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12224         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12225   
12226 </code></pre>
12227  *      
12228  */
12229 Roo.DomTemplate = function()
12230 {
12231      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12232      if (this.html) {
12233         this.compile();
12234      }
12235 };
12236
12237
12238 Roo.extend(Roo.DomTemplate, Roo.Template, {
12239     /**
12240      * id counter for sub templates.
12241      */
12242     id : 0,
12243     /**
12244      * flag to indicate if dom parser is inside a pre,
12245      * it will strip whitespace if not.
12246      */
12247     inPre : false,
12248     
12249     /**
12250      * The various sub templates
12251      */
12252     tpls : false,
12253     
12254     
12255     
12256     /**
12257      *
12258      * basic tag replacing syntax
12259      * WORD:WORD()
12260      *
12261      * // you can fake an object call by doing this
12262      *  x.t:(test,tesT) 
12263      * 
12264      */
12265     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12266     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12267     
12268     iterChild : function (node, method) {
12269         
12270         var oldPre = this.inPre;
12271         if (node.tagName == 'PRE') {
12272             this.inPre = true;
12273         }
12274         for( var i = 0; i < node.childNodes.length; i++) {
12275             method.call(this, node.childNodes[i]);
12276         }
12277         this.inPre = oldPre;
12278     },
12279     
12280     
12281     
12282     /**
12283      * compile the template
12284      *
12285      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12286      *
12287      */
12288     compile: function()
12289     {
12290         var s = this.html;
12291         
12292         // covert the html into DOM...
12293         var doc = false;
12294         var div =false;
12295         try {
12296             doc = document.implementation.createHTMLDocument("");
12297             doc.documentElement.innerHTML =   this.html  ;
12298             div = doc.documentElement;
12299         } catch (e) {
12300             // old IE... - nasty -- it causes all sorts of issues.. with
12301             // images getting pulled from server..
12302             div = document.createElement('div');
12303             div.innerHTML = this.html;
12304         }
12305         //doc.documentElement.innerHTML = htmlBody
12306          
12307         
12308         
12309         this.tpls = [];
12310         var _t = this;
12311         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12312         
12313         var tpls = this.tpls;
12314         
12315         // create a top level template from the snippet..
12316         
12317         //Roo.log(div.innerHTML);
12318         
12319         var tpl = {
12320             uid : 'master',
12321             id : this.id++,
12322             attr : false,
12323             value : false,
12324             body : div.innerHTML,
12325             
12326             forCall : false,
12327             execCall : false,
12328             dom : div,
12329             isTop : true
12330             
12331         };
12332         tpls.unshift(tpl);
12333         
12334         
12335         // compile them...
12336         this.tpls = [];
12337         Roo.each(tpls, function(tp){
12338             this.compileTpl(tp);
12339             this.tpls[tp.id] = tp;
12340         }, this);
12341         
12342         this.master = tpls[0];
12343         return this;
12344         
12345         
12346     },
12347     
12348     compileNode : function(node, istop) {
12349         // test for
12350         //Roo.log(node);
12351         
12352         
12353         // skip anything not a tag..
12354         if (node.nodeType != 1) {
12355             if (node.nodeType == 3 && !this.inPre) {
12356                 // reduce white space..
12357                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12358                 
12359             }
12360             return;
12361         }
12362         
12363         var tpl = {
12364             uid : false,
12365             id : false,
12366             attr : false,
12367             value : false,
12368             body : '',
12369             
12370             forCall : false,
12371             execCall : false,
12372             dom : false,
12373             isTop : istop
12374             
12375             
12376         };
12377         
12378         
12379         switch(true) {
12380             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12381             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12382             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12383             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12384             // no default..
12385         }
12386         
12387         
12388         if (!tpl.attr) {
12389             // just itterate children..
12390             this.iterChild(node,this.compileNode);
12391             return;
12392         }
12393         tpl.uid = this.id++;
12394         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12395         node.removeAttribute('roo-'+ tpl.attr);
12396         if (tpl.attr != 'name') {
12397             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12398             node.parentNode.replaceChild(placeholder,  node);
12399         } else {
12400             
12401             var placeholder =  document.createElement('span');
12402             placeholder.className = 'roo-tpl-' + tpl.value;
12403             node.parentNode.replaceChild(placeholder,  node);
12404         }
12405         
12406         // parent now sees '{domtplXXXX}
12407         this.iterChild(node,this.compileNode);
12408         
12409         // we should now have node body...
12410         var div = document.createElement('div');
12411         div.appendChild(node);
12412         tpl.dom = node;
12413         // this has the unfortunate side effect of converting tagged attributes
12414         // eg. href="{...}" into %7C...%7D
12415         // this has been fixed by searching for those combo's although it's a bit hacky..
12416         
12417         
12418         tpl.body = div.innerHTML;
12419         
12420         
12421          
12422         tpl.id = tpl.uid;
12423         switch(tpl.attr) {
12424             case 'for' :
12425                 switch (tpl.value) {
12426                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12427                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12428                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12429                 }
12430                 break;
12431             
12432             case 'exec':
12433                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12434                 break;
12435             
12436             case 'if':     
12437                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12438                 break;
12439             
12440             case 'name':
12441                 tpl.id  = tpl.value; // replace non characters???
12442                 break;
12443             
12444         }
12445         
12446         
12447         this.tpls.push(tpl);
12448         
12449         
12450         
12451     },
12452     
12453     
12454     
12455     
12456     /**
12457      * Compile a segment of the template into a 'sub-template'
12458      *
12459      * 
12460      * 
12461      *
12462      */
12463     compileTpl : function(tpl)
12464     {
12465         var fm = Roo.util.Format;
12466         var useF = this.disableFormats !== true;
12467         
12468         var sep = Roo.isGecko ? "+\n" : ",\n";
12469         
12470         var undef = function(str) {
12471             Roo.debug && Roo.log("Property not found :"  + str);
12472             return '';
12473         };
12474           
12475         //Roo.log(tpl.body);
12476         
12477         
12478         
12479         var fn = function(m, lbrace, name, format, args)
12480         {
12481             //Roo.log("ARGS");
12482             //Roo.log(arguments);
12483             args = args ? args.replace(/\\'/g,"'") : args;
12484             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12485             if (typeof(format) == 'undefined') {
12486                 format =  'htmlEncode'; 
12487             }
12488             if (format == 'raw' ) {
12489                 format = false;
12490             }
12491             
12492             if(name.substr(0, 6) == 'domtpl'){
12493                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12494             }
12495             
12496             // build an array of options to determine if value is undefined..
12497             
12498             // basically get 'xxxx.yyyy' then do
12499             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12500             //    (function () { Roo.log("Property not found"); return ''; })() :
12501             //    ......
12502             
12503             var udef_ar = [];
12504             var lookfor = '';
12505             Roo.each(name.split('.'), function(st) {
12506                 lookfor += (lookfor.length ? '.': '') + st;
12507                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12508             });
12509             
12510             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12511             
12512             
12513             if(format && useF){
12514                 
12515                 args = args ? ',' + args : "";
12516                  
12517                 if(format.substr(0, 5) != "this."){
12518                     format = "fm." + format + '(';
12519                 }else{
12520                     format = 'this.call("'+ format.substr(5) + '", ';
12521                     args = ", values";
12522                 }
12523                 
12524                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12525             }
12526              
12527             if (args && args.length) {
12528                 // called with xxyx.yuu:(test,test)
12529                 // change to ()
12530                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12531             }
12532             // raw.. - :raw modifier..
12533             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12534             
12535         };
12536         var body;
12537         // branched to use + in gecko and [].join() in others
12538         if(Roo.isGecko){
12539             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12540                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12541                     "';};};";
12542         }else{
12543             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12544             body.push(tpl.body.replace(/(\r\n|\n)/g,
12545                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12546             body.push("'].join('');};};");
12547             body = body.join('');
12548         }
12549         
12550         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12551        
12552         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12553         eval(body);
12554         
12555         return this;
12556     },
12557      
12558     /**
12559      * same as applyTemplate, except it's done to one of the subTemplates
12560      * when using named templates, you can do:
12561      *
12562      * var str = pl.applySubTemplate('your-name', values);
12563      *
12564      * 
12565      * @param {Number} id of the template
12566      * @param {Object} values to apply to template
12567      * @param {Object} parent (normaly the instance of this object)
12568      */
12569     applySubTemplate : function(id, values, parent)
12570     {
12571         
12572         
12573         var t = this.tpls[id];
12574         
12575         
12576         try { 
12577             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12578                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12579                 return '';
12580             }
12581         } catch(e) {
12582             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12583             Roo.log(values);
12584           
12585             return '';
12586         }
12587         try { 
12588             
12589             if(t.execCall && t.execCall.call(this, values, parent)){
12590                 return '';
12591             }
12592         } catch(e) {
12593             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12594             Roo.log(values);
12595             return '';
12596         }
12597         
12598         try {
12599             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12600             parent = t.target ? values : parent;
12601             if(t.forCall && vs instanceof Array){
12602                 var buf = [];
12603                 for(var i = 0, len = vs.length; i < len; i++){
12604                     try {
12605                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12606                     } catch (e) {
12607                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12608                         Roo.log(e.body);
12609                         //Roo.log(t.compiled);
12610                         Roo.log(vs[i]);
12611                     }   
12612                 }
12613                 return buf.join('');
12614             }
12615         } catch (e) {
12616             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12617             Roo.log(values);
12618             return '';
12619         }
12620         try {
12621             return t.compiled.call(this, vs, parent);
12622         } catch (e) {
12623             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12624             Roo.log(e.body);
12625             //Roo.log(t.compiled);
12626             Roo.log(values);
12627             return '';
12628         }
12629     },
12630
12631    
12632
12633     applyTemplate : function(values){
12634         return this.master.compiled.call(this, values, {});
12635         //var s = this.subs;
12636     },
12637
12638     apply : function(){
12639         return this.applyTemplate.apply(this, arguments);
12640     }
12641
12642  });
12643
12644 Roo.DomTemplate.from = function(el){
12645     el = Roo.getDom(el);
12646     return new Roo.Domtemplate(el.value || el.innerHTML);
12647 };/*
12648  * Based on:
12649  * Ext JS Library 1.1.1
12650  * Copyright(c) 2006-2007, Ext JS, LLC.
12651  *
12652  * Originally Released Under LGPL - original licence link has changed is not relivant.
12653  *
12654  * Fork - LGPL
12655  * <script type="text/javascript">
12656  */
12657
12658 /**
12659  * @class Roo.util.DelayedTask
12660  * Provides a convenient method of performing setTimeout where a new
12661  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12662  * You can use this class to buffer
12663  * the keypress events for a certain number of milliseconds, and perform only if they stop
12664  * for that amount of time.
12665  * @constructor The parameters to this constructor serve as defaults and are not required.
12666  * @param {Function} fn (optional) The default function to timeout
12667  * @param {Object} scope (optional) The default scope of that timeout
12668  * @param {Array} args (optional) The default Array of arguments
12669  */
12670 Roo.util.DelayedTask = function(fn, scope, args){
12671     var id = null, d, t;
12672
12673     var call = function(){
12674         var now = new Date().getTime();
12675         if(now - t >= d){
12676             clearInterval(id);
12677             id = null;
12678             fn.apply(scope, args || []);
12679         }
12680     };
12681     /**
12682      * Cancels any pending timeout and queues a new one
12683      * @param {Number} delay The milliseconds to delay
12684      * @param {Function} newFn (optional) Overrides function passed to constructor
12685      * @param {Object} newScope (optional) Overrides scope passed to constructor
12686      * @param {Array} newArgs (optional) Overrides args passed to constructor
12687      */
12688     this.delay = function(delay, newFn, newScope, newArgs){
12689         if(id && delay != d){
12690             this.cancel();
12691         }
12692         d = delay;
12693         t = new Date().getTime();
12694         fn = newFn || fn;
12695         scope = newScope || scope;
12696         args = newArgs || args;
12697         if(!id){
12698             id = setInterval(call, d);
12699         }
12700     };
12701
12702     /**
12703      * Cancel the last queued timeout
12704      */
12705     this.cancel = function(){
12706         if(id){
12707             clearInterval(id);
12708             id = null;
12709         }
12710     };
12711 };/*
12712  * Based on:
12713  * Ext JS Library 1.1.1
12714  * Copyright(c) 2006-2007, Ext JS, LLC.
12715  *
12716  * Originally Released Under LGPL - original licence link has changed is not relivant.
12717  *
12718  * Fork - LGPL
12719  * <script type="text/javascript">
12720  */
12721  
12722  
12723 Roo.util.TaskRunner = function(interval){
12724     interval = interval || 10;
12725     var tasks = [], removeQueue = [];
12726     var id = 0;
12727     var running = false;
12728
12729     var stopThread = function(){
12730         running = false;
12731         clearInterval(id);
12732         id = 0;
12733     };
12734
12735     var startThread = function(){
12736         if(!running){
12737             running = true;
12738             id = setInterval(runTasks, interval);
12739         }
12740     };
12741
12742     var removeTask = function(task){
12743         removeQueue.push(task);
12744         if(task.onStop){
12745             task.onStop();
12746         }
12747     };
12748
12749     var runTasks = function(){
12750         if(removeQueue.length > 0){
12751             for(var i = 0, len = removeQueue.length; i < len; i++){
12752                 tasks.remove(removeQueue[i]);
12753             }
12754             removeQueue = [];
12755             if(tasks.length < 1){
12756                 stopThread();
12757                 return;
12758             }
12759         }
12760         var now = new Date().getTime();
12761         for(var i = 0, len = tasks.length; i < len; ++i){
12762             var t = tasks[i];
12763             var itime = now - t.taskRunTime;
12764             if(t.interval <= itime){
12765                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12766                 t.taskRunTime = now;
12767                 if(rt === false || t.taskRunCount === t.repeat){
12768                     removeTask(t);
12769                     return;
12770                 }
12771             }
12772             if(t.duration && t.duration <= (now - t.taskStartTime)){
12773                 removeTask(t);
12774             }
12775         }
12776     };
12777
12778     /**
12779      * Queues a new task.
12780      * @param {Object} task
12781      */
12782     this.start = function(task){
12783         tasks.push(task);
12784         task.taskStartTime = new Date().getTime();
12785         task.taskRunTime = 0;
12786         task.taskRunCount = 0;
12787         startThread();
12788         return task;
12789     };
12790
12791     this.stop = function(task){
12792         removeTask(task);
12793         return task;
12794     };
12795
12796     this.stopAll = function(){
12797         stopThread();
12798         for(var i = 0, len = tasks.length; i < len; i++){
12799             if(tasks[i].onStop){
12800                 tasks[i].onStop();
12801             }
12802         }
12803         tasks = [];
12804         removeQueue = [];
12805     };
12806 };
12807
12808 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12809  * Based on:
12810  * Ext JS Library 1.1.1
12811  * Copyright(c) 2006-2007, Ext JS, LLC.
12812  *
12813  * Originally Released Under LGPL - original licence link has changed is not relivant.
12814  *
12815  * Fork - LGPL
12816  * <script type="text/javascript">
12817  */
12818
12819  
12820 /**
12821  * @class Roo.util.MixedCollection
12822  * @extends Roo.util.Observable
12823  * A Collection class that maintains both numeric indexes and keys and exposes events.
12824  * @constructor
12825  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12826  * collection (defaults to false)
12827  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12828  * and return the key value for that item.  This is used when available to look up the key on items that
12829  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12830  * equivalent to providing an implementation for the {@link #getKey} method.
12831  */
12832 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12833     this.items = [];
12834     this.map = {};
12835     this.keys = [];
12836     this.length = 0;
12837     this.addEvents({
12838         /**
12839          * @event clear
12840          * Fires when the collection is cleared.
12841          */
12842         "clear" : true,
12843         /**
12844          * @event add
12845          * Fires when an item is added to the collection.
12846          * @param {Number} index The index at which the item was added.
12847          * @param {Object} o The item added.
12848          * @param {String} key The key associated with the added item.
12849          */
12850         "add" : true,
12851         /**
12852          * @event replace
12853          * Fires when an item is replaced in the collection.
12854          * @param {String} key he key associated with the new added.
12855          * @param {Object} old The item being replaced.
12856          * @param {Object} new The new item.
12857          */
12858         "replace" : true,
12859         /**
12860          * @event remove
12861          * Fires when an item is removed from the collection.
12862          * @param {Object} o The item being removed.
12863          * @param {String} key (optional) The key associated with the removed item.
12864          */
12865         "remove" : true,
12866         "sort" : true
12867     });
12868     this.allowFunctions = allowFunctions === true;
12869     if(keyFn){
12870         this.getKey = keyFn;
12871     }
12872     Roo.util.MixedCollection.superclass.constructor.call(this);
12873 };
12874
12875 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12876     allowFunctions : false,
12877     
12878 /**
12879  * Adds an item to the collection.
12880  * @param {String} key The key to associate with the item
12881  * @param {Object} o The item to add.
12882  * @return {Object} The item added.
12883  */
12884     add : function(key, o){
12885         if(arguments.length == 1){
12886             o = arguments[0];
12887             key = this.getKey(o);
12888         }
12889         if(typeof key == "undefined" || key === null){
12890             this.length++;
12891             this.items.push(o);
12892             this.keys.push(null);
12893         }else{
12894             var old = this.map[key];
12895             if(old){
12896                 return this.replace(key, o);
12897             }
12898             this.length++;
12899             this.items.push(o);
12900             this.map[key] = o;
12901             this.keys.push(key);
12902         }
12903         this.fireEvent("add", this.length-1, o, key);
12904         return o;
12905     },
12906        
12907 /**
12908   * MixedCollection has a generic way to fetch keys if you implement getKey.
12909 <pre><code>
12910 // normal way
12911 var mc = new Roo.util.MixedCollection();
12912 mc.add(someEl.dom.id, someEl);
12913 mc.add(otherEl.dom.id, otherEl);
12914 //and so on
12915
12916 // using getKey
12917 var mc = new Roo.util.MixedCollection();
12918 mc.getKey = function(el){
12919    return el.dom.id;
12920 };
12921 mc.add(someEl);
12922 mc.add(otherEl);
12923
12924 // or via the constructor
12925 var mc = new Roo.util.MixedCollection(false, function(el){
12926    return el.dom.id;
12927 });
12928 mc.add(someEl);
12929 mc.add(otherEl);
12930 </code></pre>
12931  * @param o {Object} The item for which to find the key.
12932  * @return {Object} The key for the passed item.
12933  */
12934     getKey : function(o){
12935          return o.id; 
12936     },
12937    
12938 /**
12939  * Replaces an item in the collection.
12940  * @param {String} key The key associated with the item to replace, or the item to replace.
12941  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12942  * @return {Object}  The new item.
12943  */
12944     replace : function(key, o){
12945         if(arguments.length == 1){
12946             o = arguments[0];
12947             key = this.getKey(o);
12948         }
12949         var old = this.item(key);
12950         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12951              return this.add(key, o);
12952         }
12953         var index = this.indexOfKey(key);
12954         this.items[index] = o;
12955         this.map[key] = o;
12956         this.fireEvent("replace", key, old, o);
12957         return o;
12958     },
12959    
12960 /**
12961  * Adds all elements of an Array or an Object to the collection.
12962  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12963  * an Array of values, each of which are added to the collection.
12964  */
12965     addAll : function(objs){
12966         if(arguments.length > 1 || objs instanceof Array){
12967             var args = arguments.length > 1 ? arguments : objs;
12968             for(var i = 0, len = args.length; i < len; i++){
12969                 this.add(args[i]);
12970             }
12971         }else{
12972             for(var key in objs){
12973                 if(this.allowFunctions || typeof objs[key] != "function"){
12974                     this.add(key, objs[key]);
12975                 }
12976             }
12977         }
12978     },
12979    
12980 /**
12981  * Executes the specified function once for every item in the collection, passing each
12982  * item as the first and only parameter. returning false from the function will stop the iteration.
12983  * @param {Function} fn The function to execute for each item.
12984  * @param {Object} scope (optional) The scope in which to execute the function.
12985  */
12986     each : function(fn, scope){
12987         var items = [].concat(this.items); // each safe for removal
12988         for(var i = 0, len = items.length; i < len; i++){
12989             if(fn.call(scope || items[i], items[i], i, len) === false){
12990                 break;
12991             }
12992         }
12993     },
12994    
12995 /**
12996  * Executes the specified function once for every key in the collection, passing each
12997  * key, and its associated item as the first two parameters.
12998  * @param {Function} fn The function to execute for each item.
12999  * @param {Object} scope (optional) The scope in which to execute the function.
13000  */
13001     eachKey : function(fn, scope){
13002         for(var i = 0, len = this.keys.length; i < len; i++){
13003             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13004         }
13005     },
13006    
13007 /**
13008  * Returns the first item in the collection which elicits a true return value from the
13009  * passed selection function.
13010  * @param {Function} fn The selection function to execute for each item.
13011  * @param {Object} scope (optional) The scope in which to execute the function.
13012  * @return {Object} The first item in the collection which returned true from the selection function.
13013  */
13014     find : function(fn, scope){
13015         for(var i = 0, len = this.items.length; i < len; i++){
13016             if(fn.call(scope || window, this.items[i], this.keys[i])){
13017                 return this.items[i];
13018             }
13019         }
13020         return null;
13021     },
13022    
13023 /**
13024  * Inserts an item at the specified index in the collection.
13025  * @param {Number} index The index to insert the item at.
13026  * @param {String} key The key to associate with the new item, or the item itself.
13027  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13028  * @return {Object} The item inserted.
13029  */
13030     insert : function(index, key, o){
13031         if(arguments.length == 2){
13032             o = arguments[1];
13033             key = this.getKey(o);
13034         }
13035         if(index >= this.length){
13036             return this.add(key, o);
13037         }
13038         this.length++;
13039         this.items.splice(index, 0, o);
13040         if(typeof key != "undefined" && key != null){
13041             this.map[key] = o;
13042         }
13043         this.keys.splice(index, 0, key);
13044         this.fireEvent("add", index, o, key);
13045         return o;
13046     },
13047    
13048 /**
13049  * Removed an item from the collection.
13050  * @param {Object} o The item to remove.
13051  * @return {Object} The item removed.
13052  */
13053     remove : function(o){
13054         return this.removeAt(this.indexOf(o));
13055     },
13056    
13057 /**
13058  * Remove an item from a specified index in the collection.
13059  * @param {Number} index The index within the collection of the item to remove.
13060  */
13061     removeAt : function(index){
13062         if(index < this.length && index >= 0){
13063             this.length--;
13064             var o = this.items[index];
13065             this.items.splice(index, 1);
13066             var key = this.keys[index];
13067             if(typeof key != "undefined"){
13068                 delete this.map[key];
13069             }
13070             this.keys.splice(index, 1);
13071             this.fireEvent("remove", o, key);
13072         }
13073     },
13074    
13075 /**
13076  * Removed an item associated with the passed key fom the collection.
13077  * @param {String} key The key of the item to remove.
13078  */
13079     removeKey : function(key){
13080         return this.removeAt(this.indexOfKey(key));
13081     },
13082    
13083 /**
13084  * Returns the number of items in the collection.
13085  * @return {Number} the number of items in the collection.
13086  */
13087     getCount : function(){
13088         return this.length; 
13089     },
13090    
13091 /**
13092  * Returns index within the collection of the passed Object.
13093  * @param {Object} o The item to find the index of.
13094  * @return {Number} index of the item.
13095  */
13096     indexOf : function(o){
13097         if(!this.items.indexOf){
13098             for(var i = 0, len = this.items.length; i < len; i++){
13099                 if(this.items[i] == o) {
13100                     return i;
13101                 }
13102             }
13103             return -1;
13104         }else{
13105             return this.items.indexOf(o);
13106         }
13107     },
13108    
13109 /**
13110  * Returns index within the collection of the passed key.
13111  * @param {String} key The key to find the index of.
13112  * @return {Number} index of the key.
13113  */
13114     indexOfKey : function(key){
13115         if(!this.keys.indexOf){
13116             for(var i = 0, len = this.keys.length; i < len; i++){
13117                 if(this.keys[i] == key) {
13118                     return i;
13119                 }
13120             }
13121             return -1;
13122         }else{
13123             return this.keys.indexOf(key);
13124         }
13125     },
13126    
13127 /**
13128  * Returns the item associated with the passed key OR index. Key has priority over index.
13129  * @param {String/Number} key The key or index of the item.
13130  * @return {Object} The item associated with the passed key.
13131  */
13132     item : function(key){
13133         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13134         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13135     },
13136     
13137 /**
13138  * Returns the item at the specified index.
13139  * @param {Number} index The index of the item.
13140  * @return {Object}
13141  */
13142     itemAt : function(index){
13143         return this.items[index];
13144     },
13145     
13146 /**
13147  * Returns the item associated with the passed key.
13148  * @param {String/Number} key The key of the item.
13149  * @return {Object} The item associated with the passed key.
13150  */
13151     key : function(key){
13152         return this.map[key];
13153     },
13154    
13155 /**
13156  * Returns true if the collection contains the passed Object as an item.
13157  * @param {Object} o  The Object to look for in the collection.
13158  * @return {Boolean} True if the collection contains the Object as an item.
13159  */
13160     contains : function(o){
13161         return this.indexOf(o) != -1;
13162     },
13163    
13164 /**
13165  * Returns true if the collection contains the passed Object as a key.
13166  * @param {String} key The key to look for in the collection.
13167  * @return {Boolean} True if the collection contains the Object as a key.
13168  */
13169     containsKey : function(key){
13170         return typeof this.map[key] != "undefined";
13171     },
13172    
13173 /**
13174  * Removes all items from the collection.
13175  */
13176     clear : function(){
13177         this.length = 0;
13178         this.items = [];
13179         this.keys = [];
13180         this.map = {};
13181         this.fireEvent("clear");
13182     },
13183    
13184 /**
13185  * Returns the first item in the collection.
13186  * @return {Object} the first item in the collection..
13187  */
13188     first : function(){
13189         return this.items[0]; 
13190     },
13191    
13192 /**
13193  * Returns the last item in the collection.
13194  * @return {Object} the last item in the collection..
13195  */
13196     last : function(){
13197         return this.items[this.length-1];   
13198     },
13199     
13200     _sort : function(property, dir, fn){
13201         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13202         fn = fn || function(a, b){
13203             return a-b;
13204         };
13205         var c = [], k = this.keys, items = this.items;
13206         for(var i = 0, len = items.length; i < len; i++){
13207             c[c.length] = {key: k[i], value: items[i], index: i};
13208         }
13209         c.sort(function(a, b){
13210             var v = fn(a[property], b[property]) * dsc;
13211             if(v == 0){
13212                 v = (a.index < b.index ? -1 : 1);
13213             }
13214             return v;
13215         });
13216         for(var i = 0, len = c.length; i < len; i++){
13217             items[i] = c[i].value;
13218             k[i] = c[i].key;
13219         }
13220         this.fireEvent("sort", this);
13221     },
13222     
13223     /**
13224      * Sorts this collection with the passed comparison function
13225      * @param {String} direction (optional) "ASC" or "DESC"
13226      * @param {Function} fn (optional) comparison function
13227      */
13228     sort : function(dir, fn){
13229         this._sort("value", dir, fn);
13230     },
13231     
13232     /**
13233      * Sorts this collection by keys
13234      * @param {String} direction (optional) "ASC" or "DESC"
13235      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13236      */
13237     keySort : function(dir, fn){
13238         this._sort("key", dir, fn || function(a, b){
13239             return String(a).toUpperCase()-String(b).toUpperCase();
13240         });
13241     },
13242     
13243     /**
13244      * Returns a range of items in this collection
13245      * @param {Number} startIndex (optional) defaults to 0
13246      * @param {Number} endIndex (optional) default to the last item
13247      * @return {Array} An array of items
13248      */
13249     getRange : function(start, end){
13250         var items = this.items;
13251         if(items.length < 1){
13252             return [];
13253         }
13254         start = start || 0;
13255         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13256         var r = [];
13257         if(start <= end){
13258             for(var i = start; i <= end; i++) {
13259                     r[r.length] = items[i];
13260             }
13261         }else{
13262             for(var i = start; i >= end; i--) {
13263                     r[r.length] = items[i];
13264             }
13265         }
13266         return r;
13267     },
13268         
13269     /**
13270      * Filter the <i>objects</i> in this collection by a specific property. 
13271      * Returns a new collection that has been filtered.
13272      * @param {String} property A property on your objects
13273      * @param {String/RegExp} value Either string that the property values 
13274      * should start with or a RegExp to test against the property
13275      * @return {MixedCollection} The new filtered collection
13276      */
13277     filter : function(property, value){
13278         if(!value.exec){ // not a regex
13279             value = String(value);
13280             if(value.length == 0){
13281                 return this.clone();
13282             }
13283             value = new RegExp("^" + Roo.escapeRe(value), "i");
13284         }
13285         return this.filterBy(function(o){
13286             return o && value.test(o[property]);
13287         });
13288         },
13289     
13290     /**
13291      * Filter by a function. * Returns a new collection that has been filtered.
13292      * The passed function will be called with each 
13293      * object in the collection. If the function returns true, the value is included 
13294      * otherwise it is filtered.
13295      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13296      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13297      * @return {MixedCollection} The new filtered collection
13298      */
13299     filterBy : function(fn, scope){
13300         var r = new Roo.util.MixedCollection();
13301         r.getKey = this.getKey;
13302         var k = this.keys, it = this.items;
13303         for(var i = 0, len = it.length; i < len; i++){
13304             if(fn.call(scope||this, it[i], k[i])){
13305                                 r.add(k[i], it[i]);
13306                         }
13307         }
13308         return r;
13309     },
13310     
13311     /**
13312      * Creates a duplicate of this collection
13313      * @return {MixedCollection}
13314      */
13315     clone : function(){
13316         var r = new Roo.util.MixedCollection();
13317         var k = this.keys, it = this.items;
13318         for(var i = 0, len = it.length; i < len; i++){
13319             r.add(k[i], it[i]);
13320         }
13321         r.getKey = this.getKey;
13322         return r;
13323     }
13324 });
13325 /**
13326  * Returns the item associated with the passed key or index.
13327  * @method
13328  * @param {String/Number} key The key or index of the item.
13329  * @return {Object} The item associated with the passed key.
13330  */
13331 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13332  * Based on:
13333  * Ext JS Library 1.1.1
13334  * Copyright(c) 2006-2007, Ext JS, LLC.
13335  *
13336  * Originally Released Under LGPL - original licence link has changed is not relivant.
13337  *
13338  * Fork - LGPL
13339  * <script type="text/javascript">
13340  */
13341 /**
13342  * @class Roo.util.JSON
13343  * Modified version of Douglas Crockford"s json.js that doesn"t
13344  * mess with the Object prototype 
13345  * http://www.json.org/js.html
13346  * @singleton
13347  */
13348 Roo.util.JSON = new (function(){
13349     var useHasOwn = {}.hasOwnProperty ? true : false;
13350     
13351     // crashes Safari in some instances
13352     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13353     
13354     var pad = function(n) {
13355         return n < 10 ? "0" + n : n;
13356     };
13357     
13358     var m = {
13359         "\b": '\\b',
13360         "\t": '\\t',
13361         "\n": '\\n',
13362         "\f": '\\f',
13363         "\r": '\\r',
13364         '"' : '\\"',
13365         "\\": '\\\\'
13366     };
13367
13368     var encodeString = function(s){
13369         if (/["\\\x00-\x1f]/.test(s)) {
13370             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13371                 var c = m[b];
13372                 if(c){
13373                     return c;
13374                 }
13375                 c = b.charCodeAt();
13376                 return "\\u00" +
13377                     Math.floor(c / 16).toString(16) +
13378                     (c % 16).toString(16);
13379             }) + '"';
13380         }
13381         return '"' + s + '"';
13382     };
13383     
13384     var encodeArray = function(o){
13385         var a = ["["], b, i, l = o.length, v;
13386             for (i = 0; i < l; i += 1) {
13387                 v = o[i];
13388                 switch (typeof v) {
13389                     case "undefined":
13390                     case "function":
13391                     case "unknown":
13392                         break;
13393                     default:
13394                         if (b) {
13395                             a.push(',');
13396                         }
13397                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13398                         b = true;
13399                 }
13400             }
13401             a.push("]");
13402             return a.join("");
13403     };
13404     
13405     var encodeDate = function(o){
13406         return '"' + o.getFullYear() + "-" +
13407                 pad(o.getMonth() + 1) + "-" +
13408                 pad(o.getDate()) + "T" +
13409                 pad(o.getHours()) + ":" +
13410                 pad(o.getMinutes()) + ":" +
13411                 pad(o.getSeconds()) + '"';
13412     };
13413     
13414     /**
13415      * Encodes an Object, Array or other value
13416      * @param {Mixed} o The variable to encode
13417      * @return {String} The JSON string
13418      */
13419     this.encode = function(o)
13420     {
13421         // should this be extended to fully wrap stringify..
13422         
13423         if(typeof o == "undefined" || o === null){
13424             return "null";
13425         }else if(o instanceof Array){
13426             return encodeArray(o);
13427         }else if(o instanceof Date){
13428             return encodeDate(o);
13429         }else if(typeof o == "string"){
13430             return encodeString(o);
13431         }else if(typeof o == "number"){
13432             return isFinite(o) ? String(o) : "null";
13433         }else if(typeof o == "boolean"){
13434             return String(o);
13435         }else {
13436             var a = ["{"], b, i, v;
13437             for (i in o) {
13438                 if(!useHasOwn || o.hasOwnProperty(i)) {
13439                     v = o[i];
13440                     switch (typeof v) {
13441                     case "undefined":
13442                     case "function":
13443                     case "unknown":
13444                         break;
13445                     default:
13446                         if(b){
13447                             a.push(',');
13448                         }
13449                         a.push(this.encode(i), ":",
13450                                 v === null ? "null" : this.encode(v));
13451                         b = true;
13452                     }
13453                 }
13454             }
13455             a.push("}");
13456             return a.join("");
13457         }
13458     };
13459     
13460     /**
13461      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13462      * @param {String} json The JSON string
13463      * @return {Object} The resulting object
13464      */
13465     this.decode = function(json){
13466         
13467         return  /** eval:var:json */ eval("(" + json + ')');
13468     };
13469 })();
13470 /** 
13471  * Shorthand for {@link Roo.util.JSON#encode}
13472  * @member Roo encode 
13473  * @method */
13474 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13475 /** 
13476  * Shorthand for {@link Roo.util.JSON#decode}
13477  * @member Roo decode 
13478  * @method */
13479 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13480 /*
13481  * Based on:
13482  * Ext JS Library 1.1.1
13483  * Copyright(c) 2006-2007, Ext JS, LLC.
13484  *
13485  * Originally Released Under LGPL - original licence link has changed is not relivant.
13486  *
13487  * Fork - LGPL
13488  * <script type="text/javascript">
13489  */
13490  
13491 /**
13492  * @class Roo.util.Format
13493  * Reusable data formatting functions
13494  * @singleton
13495  */
13496 Roo.util.Format = function(){
13497     var trimRe = /^\s+|\s+$/g;
13498     return {
13499         /**
13500          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13501          * @param {String} value The string to truncate
13502          * @param {Number} length The maximum length to allow before truncating
13503          * @return {String} The converted text
13504          */
13505         ellipsis : function(value, len){
13506             if(value && value.length > len){
13507                 return value.substr(0, len-3)+"...";
13508             }
13509             return value;
13510         },
13511
13512         /**
13513          * Checks a reference and converts it to empty string if it is undefined
13514          * @param {Mixed} value Reference to check
13515          * @return {Mixed} Empty string if converted, otherwise the original value
13516          */
13517         undef : function(value){
13518             return typeof value != "undefined" ? value : "";
13519         },
13520
13521         /**
13522          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13523          * @param {String} value The string to encode
13524          * @return {String} The encoded text
13525          */
13526         htmlEncode : function(value){
13527             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13528         },
13529
13530         /**
13531          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13532          * @param {String} value The string to decode
13533          * @return {String} The decoded text
13534          */
13535         htmlDecode : function(value){
13536             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13537         },
13538
13539         /**
13540          * Trims any whitespace from either side of a string
13541          * @param {String} value The text to trim
13542          * @return {String} The trimmed text
13543          */
13544         trim : function(value){
13545             return String(value).replace(trimRe, "");
13546         },
13547
13548         /**
13549          * Returns a substring from within an original string
13550          * @param {String} value The original text
13551          * @param {Number} start The start index of the substring
13552          * @param {Number} length The length of the substring
13553          * @return {String} The substring
13554          */
13555         substr : function(value, start, length){
13556             return String(value).substr(start, length);
13557         },
13558
13559         /**
13560          * Converts a string to all lower case letters
13561          * @param {String} value The text to convert
13562          * @return {String} The converted text
13563          */
13564         lowercase : function(value){
13565             return String(value).toLowerCase();
13566         },
13567
13568         /**
13569          * Converts a string to all upper case letters
13570          * @param {String} value The text to convert
13571          * @return {String} The converted text
13572          */
13573         uppercase : function(value){
13574             return String(value).toUpperCase();
13575         },
13576
13577         /**
13578          * Converts the first character only of a string to upper case
13579          * @param {String} value The text to convert
13580          * @return {String} The converted text
13581          */
13582         capitalize : function(value){
13583             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13584         },
13585
13586         // private
13587         call : function(value, fn){
13588             if(arguments.length > 2){
13589                 var args = Array.prototype.slice.call(arguments, 2);
13590                 args.unshift(value);
13591                  
13592                 return /** eval:var:value */  eval(fn).apply(window, args);
13593             }else{
13594                 /** eval:var:value */
13595                 return /** eval:var:value */ eval(fn).call(window, value);
13596             }
13597         },
13598
13599        
13600         /**
13601          * safer version of Math.toFixed..??/
13602          * @param {Number/String} value The numeric value to format
13603          * @param {Number/String} value Decimal places 
13604          * @return {String} The formatted currency string
13605          */
13606         toFixed : function(v, n)
13607         {
13608             // why not use to fixed - precision is buggered???
13609             if (!n) {
13610                 return Math.round(v-0);
13611             }
13612             var fact = Math.pow(10,n+1);
13613             v = (Math.round((v-0)*fact))/fact;
13614             var z = (''+fact).substring(2);
13615             if (v == Math.floor(v)) {
13616                 return Math.floor(v) + '.' + z;
13617             }
13618             
13619             // now just padd decimals..
13620             var ps = String(v).split('.');
13621             var fd = (ps[1] + z);
13622             var r = fd.substring(0,n); 
13623             var rm = fd.substring(n); 
13624             if (rm < 5) {
13625                 return ps[0] + '.' + r;
13626             }
13627             r*=1; // turn it into a number;
13628             r++;
13629             if (String(r).length != n) {
13630                 ps[0]*=1;
13631                 ps[0]++;
13632                 r = String(r).substring(1); // chop the end off.
13633             }
13634             
13635             return ps[0] + '.' + r;
13636              
13637         },
13638         
13639         /**
13640          * Format a number as US currency
13641          * @param {Number/String} value The numeric value to format
13642          * @return {String} The formatted currency string
13643          */
13644         usMoney : function(v){
13645             return '$' + Roo.util.Format.number(v);
13646         },
13647         
13648         /**
13649          * Format a number
13650          * eventually this should probably emulate php's number_format
13651          * @param {Number/String} value The numeric value to format
13652          * @param {Number} decimals number of decimal places
13653          * @return {String} The formatted currency string
13654          */
13655         number : function(v,decimals)
13656         {
13657             // multiply and round.
13658             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13659             var mul = Math.pow(10, decimals);
13660             var zero = String(mul).substring(1);
13661             v = (Math.round((v-0)*mul))/mul;
13662             
13663             // if it's '0' number.. then
13664             
13665             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13666             v = String(v);
13667             var ps = v.split('.');
13668             var whole = ps[0];
13669             
13670             
13671             var r = /(\d+)(\d{3})/;
13672             // add comma's
13673             while (r.test(whole)) {
13674                 whole = whole.replace(r, '$1' + ',' + '$2');
13675             }
13676             
13677             
13678             var sub = ps[1] ?
13679                     // has decimals..
13680                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13681                     // does not have decimals
13682                     (decimals ? ('.' + zero) : '');
13683             
13684             
13685             return whole + sub ;
13686         },
13687         
13688         /**
13689          * Parse a value into a formatted date using the specified format pattern.
13690          * @param {Mixed} value The value to format
13691          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13692          * @return {String} The formatted date string
13693          */
13694         date : function(v, format){
13695             if(!v){
13696                 return "";
13697             }
13698             if(!(v instanceof Date)){
13699                 v = new Date(Date.parse(v));
13700             }
13701             return v.dateFormat(format || Roo.util.Format.defaults.date);
13702         },
13703
13704         /**
13705          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13706          * @param {String} format Any valid date format string
13707          * @return {Function} The date formatting function
13708          */
13709         dateRenderer : function(format){
13710             return function(v){
13711                 return Roo.util.Format.date(v, format);  
13712             };
13713         },
13714
13715         // private
13716         stripTagsRE : /<\/?[^>]+>/gi,
13717         
13718         /**
13719          * Strips all HTML tags
13720          * @param {Mixed} value The text from which to strip tags
13721          * @return {String} The stripped text
13722          */
13723         stripTags : function(v){
13724             return !v ? v : String(v).replace(this.stripTagsRE, "");
13725         }
13726     };
13727 }();
13728 Roo.util.Format.defaults = {
13729     date : 'd/M/Y'
13730 };/*
13731  * Based on:
13732  * Ext JS Library 1.1.1
13733  * Copyright(c) 2006-2007, Ext JS, LLC.
13734  *
13735  * Originally Released Under LGPL - original licence link has changed is not relivant.
13736  *
13737  * Fork - LGPL
13738  * <script type="text/javascript">
13739  */
13740
13741
13742  
13743
13744 /**
13745  * @class Roo.MasterTemplate
13746  * @extends Roo.Template
13747  * Provides a template that can have child templates. The syntax is:
13748 <pre><code>
13749 var t = new Roo.MasterTemplate(
13750         '&lt;select name="{name}"&gt;',
13751                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13752         '&lt;/select&gt;'
13753 );
13754 t.add('options', {value: 'foo', text: 'bar'});
13755 // or you can add multiple child elements in one shot
13756 t.addAll('options', [
13757     {value: 'foo', text: 'bar'},
13758     {value: 'foo2', text: 'bar2'},
13759     {value: 'foo3', text: 'bar3'}
13760 ]);
13761 // then append, applying the master template values
13762 t.append('my-form', {name: 'my-select'});
13763 </code></pre>
13764 * A name attribute for the child template is not required if you have only one child
13765 * template or you want to refer to them by index.
13766  */
13767 Roo.MasterTemplate = function(){
13768     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13769     this.originalHtml = this.html;
13770     var st = {};
13771     var m, re = this.subTemplateRe;
13772     re.lastIndex = 0;
13773     var subIndex = 0;
13774     while(m = re.exec(this.html)){
13775         var name = m[1], content = m[2];
13776         st[subIndex] = {
13777             name: name,
13778             index: subIndex,
13779             buffer: [],
13780             tpl : new Roo.Template(content)
13781         };
13782         if(name){
13783             st[name] = st[subIndex];
13784         }
13785         st[subIndex].tpl.compile();
13786         st[subIndex].tpl.call = this.call.createDelegate(this);
13787         subIndex++;
13788     }
13789     this.subCount = subIndex;
13790     this.subs = st;
13791 };
13792 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13793     /**
13794     * The regular expression used to match sub templates
13795     * @type RegExp
13796     * @property
13797     */
13798     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13799
13800     /**
13801      * Applies the passed values to a child template.
13802      * @param {String/Number} name (optional) The name or index of the child template
13803      * @param {Array/Object} values The values to be applied to the template
13804      * @return {MasterTemplate} this
13805      */
13806      add : function(name, values){
13807         if(arguments.length == 1){
13808             values = arguments[0];
13809             name = 0;
13810         }
13811         var s = this.subs[name];
13812         s.buffer[s.buffer.length] = s.tpl.apply(values);
13813         return this;
13814     },
13815
13816     /**
13817      * Applies all the passed values to a child template.
13818      * @param {String/Number} name (optional) The name or index of the child template
13819      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13820      * @param {Boolean} reset (optional) True to reset the template first
13821      * @return {MasterTemplate} this
13822      */
13823     fill : function(name, values, reset){
13824         var a = arguments;
13825         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13826             values = a[0];
13827             name = 0;
13828             reset = a[1];
13829         }
13830         if(reset){
13831             this.reset();
13832         }
13833         for(var i = 0, len = values.length; i < len; i++){
13834             this.add(name, values[i]);
13835         }
13836         return this;
13837     },
13838
13839     /**
13840      * Resets the template for reuse
13841      * @return {MasterTemplate} this
13842      */
13843      reset : function(){
13844         var s = this.subs;
13845         for(var i = 0; i < this.subCount; i++){
13846             s[i].buffer = [];
13847         }
13848         return this;
13849     },
13850
13851     applyTemplate : function(values){
13852         var s = this.subs;
13853         var replaceIndex = -1;
13854         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13855             return s[++replaceIndex].buffer.join("");
13856         });
13857         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13858     },
13859
13860     apply : function(){
13861         return this.applyTemplate.apply(this, arguments);
13862     },
13863
13864     compile : function(){return this;}
13865 });
13866
13867 /**
13868  * Alias for fill().
13869  * @method
13870  */
13871 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13872  /**
13873  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13874  * var tpl = Roo.MasterTemplate.from('element-id');
13875  * @param {String/HTMLElement} el
13876  * @param {Object} config
13877  * @static
13878  */
13879 Roo.MasterTemplate.from = function(el, config){
13880     el = Roo.getDom(el);
13881     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13882 };/*
13883  * Based on:
13884  * Ext JS Library 1.1.1
13885  * Copyright(c) 2006-2007, Ext JS, LLC.
13886  *
13887  * Originally Released Under LGPL - original licence link has changed is not relivant.
13888  *
13889  * Fork - LGPL
13890  * <script type="text/javascript">
13891  */
13892
13893  
13894 /**
13895  * @class Roo.util.CSS
13896  * Utility class for manipulating CSS rules
13897  * @singleton
13898  */
13899 Roo.util.CSS = function(){
13900         var rules = null;
13901         var doc = document;
13902
13903     var camelRe = /(-[a-z])/gi;
13904     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13905
13906    return {
13907    /**
13908     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13909     * tag and appended to the HEAD of the document.
13910     * @param {String|Object} cssText The text containing the css rules
13911     * @param {String} id An id to add to the stylesheet for later removal
13912     * @return {StyleSheet}
13913     */
13914     createStyleSheet : function(cssText, id){
13915         var ss;
13916         var head = doc.getElementsByTagName("head")[0];
13917         var nrules = doc.createElement("style");
13918         nrules.setAttribute("type", "text/css");
13919         if(id){
13920             nrules.setAttribute("id", id);
13921         }
13922         if (typeof(cssText) != 'string') {
13923             // support object maps..
13924             // not sure if this a good idea.. 
13925             // perhaps it should be merged with the general css handling
13926             // and handle js style props.
13927             var cssTextNew = [];
13928             for(var n in cssText) {
13929                 var citems = [];
13930                 for(var k in cssText[n]) {
13931                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13932                 }
13933                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13934                 
13935             }
13936             cssText = cssTextNew.join("\n");
13937             
13938         }
13939        
13940        
13941        if(Roo.isIE){
13942            head.appendChild(nrules);
13943            ss = nrules.styleSheet;
13944            ss.cssText = cssText;
13945        }else{
13946            try{
13947                 nrules.appendChild(doc.createTextNode(cssText));
13948            }catch(e){
13949                nrules.cssText = cssText; 
13950            }
13951            head.appendChild(nrules);
13952            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13953        }
13954        this.cacheStyleSheet(ss);
13955        return ss;
13956    },
13957
13958    /**
13959     * Removes a style or link tag by id
13960     * @param {String} id The id of the tag
13961     */
13962    removeStyleSheet : function(id){
13963        var existing = doc.getElementById(id);
13964        if(existing){
13965            existing.parentNode.removeChild(existing);
13966        }
13967    },
13968
13969    /**
13970     * Dynamically swaps an existing stylesheet reference for a new one
13971     * @param {String} id The id of an existing link tag to remove
13972     * @param {String} url The href of the new stylesheet to include
13973     */
13974    swapStyleSheet : function(id, url){
13975        this.removeStyleSheet(id);
13976        var ss = doc.createElement("link");
13977        ss.setAttribute("rel", "stylesheet");
13978        ss.setAttribute("type", "text/css");
13979        ss.setAttribute("id", id);
13980        ss.setAttribute("href", url);
13981        doc.getElementsByTagName("head")[0].appendChild(ss);
13982    },
13983    
13984    /**
13985     * Refresh the rule cache if you have dynamically added stylesheets
13986     * @return {Object} An object (hash) of rules indexed by selector
13987     */
13988    refreshCache : function(){
13989        return this.getRules(true);
13990    },
13991
13992    // private
13993    cacheStyleSheet : function(stylesheet){
13994        if(!rules){
13995            rules = {};
13996        }
13997        try{// try catch for cross domain access issue
13998            var ssRules = stylesheet.cssRules || stylesheet.rules;
13999            for(var j = ssRules.length-1; j >= 0; --j){
14000                rules[ssRules[j].selectorText] = ssRules[j];
14001            }
14002        }catch(e){}
14003    },
14004    
14005    /**
14006     * Gets all css rules for the document
14007     * @param {Boolean} refreshCache true to refresh the internal cache
14008     * @return {Object} An object (hash) of rules indexed by selector
14009     */
14010    getRules : function(refreshCache){
14011                 if(rules == null || refreshCache){
14012                         rules = {};
14013                         var ds = doc.styleSheets;
14014                         for(var i =0, len = ds.length; i < len; i++){
14015                             try{
14016                         this.cacheStyleSheet(ds[i]);
14017                     }catch(e){} 
14018                 }
14019                 }
14020                 return rules;
14021         },
14022         
14023         /**
14024     * Gets an an individual CSS rule by selector(s)
14025     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14026     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14027     * @return {CSSRule} The CSS rule or null if one is not found
14028     */
14029    getRule : function(selector, refreshCache){
14030                 var rs = this.getRules(refreshCache);
14031                 if(!(selector instanceof Array)){
14032                     return rs[selector];
14033                 }
14034                 for(var i = 0; i < selector.length; i++){
14035                         if(rs[selector[i]]){
14036                                 return rs[selector[i]];
14037                         }
14038                 }
14039                 return null;
14040         },
14041         
14042         
14043         /**
14044     * Updates a rule property
14045     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14046     * @param {String} property The css property
14047     * @param {String} value The new value for the property
14048     * @return {Boolean} true If a rule was found and updated
14049     */
14050    updateRule : function(selector, property, value){
14051                 if(!(selector instanceof Array)){
14052                         var rule = this.getRule(selector);
14053                         if(rule){
14054                                 rule.style[property.replace(camelRe, camelFn)] = value;
14055                                 return true;
14056                         }
14057                 }else{
14058                         for(var i = 0; i < selector.length; i++){
14059                                 if(this.updateRule(selector[i], property, value)){
14060                                         return true;
14061                                 }
14062                         }
14063                 }
14064                 return false;
14065         }
14066    };   
14067 }();/*
14068  * Based on:
14069  * Ext JS Library 1.1.1
14070  * Copyright(c) 2006-2007, Ext JS, LLC.
14071  *
14072  * Originally Released Under LGPL - original licence link has changed is not relivant.
14073  *
14074  * Fork - LGPL
14075  * <script type="text/javascript">
14076  */
14077
14078  
14079
14080 /**
14081  * @class Roo.util.ClickRepeater
14082  * @extends Roo.util.Observable
14083  * 
14084  * A wrapper class which can be applied to any element. Fires a "click" event while the
14085  * mouse is pressed. The interval between firings may be specified in the config but
14086  * defaults to 10 milliseconds.
14087  * 
14088  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14089  * 
14090  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14091  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14092  * Similar to an autorepeat key delay.
14093  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14094  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14095  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14096  *           "interval" and "delay" are ignored. "immediate" is honored.
14097  * @cfg {Boolean} preventDefault True to prevent the default click event
14098  * @cfg {Boolean} stopDefault True to stop the default click event
14099  * 
14100  * @history
14101  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14102  *     2007-02-02 jvs Renamed to ClickRepeater
14103  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14104  *
14105  *  @constructor
14106  * @param {String/HTMLElement/Element} el The element to listen on
14107  * @param {Object} config
14108  **/
14109 Roo.util.ClickRepeater = function(el, config)
14110 {
14111     this.el = Roo.get(el);
14112     this.el.unselectable();
14113
14114     Roo.apply(this, config);
14115
14116     this.addEvents({
14117     /**
14118      * @event mousedown
14119      * Fires when the mouse button is depressed.
14120      * @param {Roo.util.ClickRepeater} this
14121      */
14122         "mousedown" : true,
14123     /**
14124      * @event click
14125      * Fires on a specified interval during the time the element is pressed.
14126      * @param {Roo.util.ClickRepeater} this
14127      */
14128         "click" : true,
14129     /**
14130      * @event mouseup
14131      * Fires when the mouse key is released.
14132      * @param {Roo.util.ClickRepeater} this
14133      */
14134         "mouseup" : true
14135     });
14136
14137     this.el.on("mousedown", this.handleMouseDown, this);
14138     if(this.preventDefault || this.stopDefault){
14139         this.el.on("click", function(e){
14140             if(this.preventDefault){
14141                 e.preventDefault();
14142             }
14143             if(this.stopDefault){
14144                 e.stopEvent();
14145             }
14146         }, this);
14147     }
14148
14149     // allow inline handler
14150     if(this.handler){
14151         this.on("click", this.handler,  this.scope || this);
14152     }
14153
14154     Roo.util.ClickRepeater.superclass.constructor.call(this);
14155 };
14156
14157 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14158     interval : 20,
14159     delay: 250,
14160     preventDefault : true,
14161     stopDefault : false,
14162     timer : 0,
14163
14164     // private
14165     handleMouseDown : function(){
14166         clearTimeout(this.timer);
14167         this.el.blur();
14168         if(this.pressClass){
14169             this.el.addClass(this.pressClass);
14170         }
14171         this.mousedownTime = new Date();
14172
14173         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14174         this.el.on("mouseout", this.handleMouseOut, this);
14175
14176         this.fireEvent("mousedown", this);
14177         this.fireEvent("click", this);
14178         
14179         this.timer = this.click.defer(this.delay || this.interval, this);
14180     },
14181
14182     // private
14183     click : function(){
14184         this.fireEvent("click", this);
14185         this.timer = this.click.defer(this.getInterval(), this);
14186     },
14187
14188     // private
14189     getInterval: function(){
14190         if(!this.accelerate){
14191             return this.interval;
14192         }
14193         var pressTime = this.mousedownTime.getElapsed();
14194         if(pressTime < 500){
14195             return 400;
14196         }else if(pressTime < 1700){
14197             return 320;
14198         }else if(pressTime < 2600){
14199             return 250;
14200         }else if(pressTime < 3500){
14201             return 180;
14202         }else if(pressTime < 4400){
14203             return 140;
14204         }else if(pressTime < 5300){
14205             return 80;
14206         }else if(pressTime < 6200){
14207             return 50;
14208         }else{
14209             return 10;
14210         }
14211     },
14212
14213     // private
14214     handleMouseOut : function(){
14215         clearTimeout(this.timer);
14216         if(this.pressClass){
14217             this.el.removeClass(this.pressClass);
14218         }
14219         this.el.on("mouseover", this.handleMouseReturn, this);
14220     },
14221
14222     // private
14223     handleMouseReturn : function(){
14224         this.el.un("mouseover", this.handleMouseReturn);
14225         if(this.pressClass){
14226             this.el.addClass(this.pressClass);
14227         }
14228         this.click();
14229     },
14230
14231     // private
14232     handleMouseUp : function(){
14233         clearTimeout(this.timer);
14234         this.el.un("mouseover", this.handleMouseReturn);
14235         this.el.un("mouseout", this.handleMouseOut);
14236         Roo.get(document).un("mouseup", this.handleMouseUp);
14237         this.el.removeClass(this.pressClass);
14238         this.fireEvent("mouseup", this);
14239     }
14240 });/*
14241  * Based on:
14242  * Ext JS Library 1.1.1
14243  * Copyright(c) 2006-2007, Ext JS, LLC.
14244  *
14245  * Originally Released Under LGPL - original licence link has changed is not relivant.
14246  *
14247  * Fork - LGPL
14248  * <script type="text/javascript">
14249  */
14250
14251  
14252 /**
14253  * @class Roo.KeyNav
14254  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14255  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14256  * way to implement custom navigation schemes for any UI component.</p>
14257  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14258  * pageUp, pageDown, del, home, end.  Usage:</p>
14259  <pre><code>
14260 var nav = new Roo.KeyNav("my-element", {
14261     "left" : function(e){
14262         this.moveLeft(e.ctrlKey);
14263     },
14264     "right" : function(e){
14265         this.moveRight(e.ctrlKey);
14266     },
14267     "enter" : function(e){
14268         this.save();
14269     },
14270     scope : this
14271 });
14272 </code></pre>
14273  * @constructor
14274  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14275  * @param {Object} config The config
14276  */
14277 Roo.KeyNav = function(el, config){
14278     this.el = Roo.get(el);
14279     Roo.apply(this, config);
14280     if(!this.disabled){
14281         this.disabled = true;
14282         this.enable();
14283     }
14284 };
14285
14286 Roo.KeyNav.prototype = {
14287     /**
14288      * @cfg {Boolean} disabled
14289      * True to disable this KeyNav instance (defaults to false)
14290      */
14291     disabled : false,
14292     /**
14293      * @cfg {String} defaultEventAction
14294      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14295      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14296      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14297      */
14298     defaultEventAction: "stopEvent",
14299     /**
14300      * @cfg {Boolean} forceKeyDown
14301      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14302      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14303      * handle keydown instead of keypress.
14304      */
14305     forceKeyDown : false,
14306
14307     // private
14308     prepareEvent : function(e){
14309         var k = e.getKey();
14310         var h = this.keyToHandler[k];
14311         //if(h && this[h]){
14312         //    e.stopPropagation();
14313         //}
14314         if(Roo.isSafari && h && k >= 37 && k <= 40){
14315             e.stopEvent();
14316         }
14317     },
14318
14319     // private
14320     relay : function(e){
14321         var k = e.getKey();
14322         var h = this.keyToHandler[k];
14323         if(h && this[h]){
14324             if(this.doRelay(e, this[h], h) !== true){
14325                 e[this.defaultEventAction]();
14326             }
14327         }
14328     },
14329
14330     // private
14331     doRelay : function(e, h, hname){
14332         return h.call(this.scope || this, e);
14333     },
14334
14335     // possible handlers
14336     enter : false,
14337     left : false,
14338     right : false,
14339     up : false,
14340     down : false,
14341     tab : false,
14342     esc : false,
14343     pageUp : false,
14344     pageDown : false,
14345     del : false,
14346     home : false,
14347     end : false,
14348
14349     // quick lookup hash
14350     keyToHandler : {
14351         37 : "left",
14352         39 : "right",
14353         38 : "up",
14354         40 : "down",
14355         33 : "pageUp",
14356         34 : "pageDown",
14357         46 : "del",
14358         36 : "home",
14359         35 : "end",
14360         13 : "enter",
14361         27 : "esc",
14362         9  : "tab"
14363     },
14364
14365         /**
14366          * Enable this KeyNav
14367          */
14368         enable: function(){
14369                 if(this.disabled){
14370             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14371             // the EventObject will normalize Safari automatically
14372             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14373                 this.el.on("keydown", this.relay,  this);
14374             }else{
14375                 this.el.on("keydown", this.prepareEvent,  this);
14376                 this.el.on("keypress", this.relay,  this);
14377             }
14378                     this.disabled = false;
14379                 }
14380         },
14381
14382         /**
14383          * Disable this KeyNav
14384          */
14385         disable: function(){
14386                 if(!this.disabled){
14387                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14388                 this.el.un("keydown", this.relay);
14389             }else{
14390                 this.el.un("keydown", this.prepareEvent);
14391                 this.el.un("keypress", this.relay);
14392             }
14393                     this.disabled = true;
14394                 }
14395         }
14396 };/*
14397  * Based on:
14398  * Ext JS Library 1.1.1
14399  * Copyright(c) 2006-2007, Ext JS, LLC.
14400  *
14401  * Originally Released Under LGPL - original licence link has changed is not relivant.
14402  *
14403  * Fork - LGPL
14404  * <script type="text/javascript">
14405  */
14406
14407  
14408 /**
14409  * @class Roo.KeyMap
14410  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14411  * The constructor accepts the same config object as defined by {@link #addBinding}.
14412  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14413  * combination it will call the function with this signature (if the match is a multi-key
14414  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14415  * A KeyMap can also handle a string representation of keys.<br />
14416  * Usage:
14417  <pre><code>
14418 // map one key by key code
14419 var map = new Roo.KeyMap("my-element", {
14420     key: 13, // or Roo.EventObject.ENTER
14421     fn: myHandler,
14422     scope: myObject
14423 });
14424
14425 // map multiple keys to one action by string
14426 var map = new Roo.KeyMap("my-element", {
14427     key: "a\r\n\t",
14428     fn: myHandler,
14429     scope: myObject
14430 });
14431
14432 // map multiple keys to multiple actions by strings and array of codes
14433 var map = new Roo.KeyMap("my-element", [
14434     {
14435         key: [10,13],
14436         fn: function(){ alert("Return was pressed"); }
14437     }, {
14438         key: "abc",
14439         fn: function(){ alert('a, b or c was pressed'); }
14440     }, {
14441         key: "\t",
14442         ctrl:true,
14443         shift:true,
14444         fn: function(){ alert('Control + shift + tab was pressed.'); }
14445     }
14446 ]);
14447 </code></pre>
14448  * <b>Note: A KeyMap starts enabled</b>
14449  * @constructor
14450  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14451  * @param {Object} config The config (see {@link #addBinding})
14452  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14453  */
14454 Roo.KeyMap = function(el, config, eventName){
14455     this.el  = Roo.get(el);
14456     this.eventName = eventName || "keydown";
14457     this.bindings = [];
14458     if(config){
14459         this.addBinding(config);
14460     }
14461     this.enable();
14462 };
14463
14464 Roo.KeyMap.prototype = {
14465     /**
14466      * True to stop the event from bubbling and prevent the default browser action if the
14467      * key was handled by the KeyMap (defaults to false)
14468      * @type Boolean
14469      */
14470     stopEvent : false,
14471
14472     /**
14473      * Add a new binding to this KeyMap. The following config object properties are supported:
14474      * <pre>
14475 Property    Type             Description
14476 ----------  ---------------  ----------------------------------------------------------------------
14477 key         String/Array     A single keycode or an array of keycodes to handle
14478 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14479 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14480 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14481 fn          Function         The function to call when KeyMap finds the expected key combination
14482 scope       Object           The scope of the callback function
14483 </pre>
14484      *
14485      * Usage:
14486      * <pre><code>
14487 // Create a KeyMap
14488 var map = new Roo.KeyMap(document, {
14489     key: Roo.EventObject.ENTER,
14490     fn: handleKey,
14491     scope: this
14492 });
14493
14494 //Add a new binding to the existing KeyMap later
14495 map.addBinding({
14496     key: 'abc',
14497     shift: true,
14498     fn: handleKey,
14499     scope: this
14500 });
14501 </code></pre>
14502      * @param {Object/Array} config A single KeyMap config or an array of configs
14503      */
14504         addBinding : function(config){
14505         if(config instanceof Array){
14506             for(var i = 0, len = config.length; i < len; i++){
14507                 this.addBinding(config[i]);
14508             }
14509             return;
14510         }
14511         var keyCode = config.key,
14512             shift = config.shift, 
14513             ctrl = config.ctrl, 
14514             alt = config.alt,
14515             fn = config.fn,
14516             scope = config.scope;
14517         if(typeof keyCode == "string"){
14518             var ks = [];
14519             var keyString = keyCode.toUpperCase();
14520             for(var j = 0, len = keyString.length; j < len; j++){
14521                 ks.push(keyString.charCodeAt(j));
14522             }
14523             keyCode = ks;
14524         }
14525         var keyArray = keyCode instanceof Array;
14526         var handler = function(e){
14527             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14528                 var k = e.getKey();
14529                 if(keyArray){
14530                     for(var i = 0, len = keyCode.length; i < len; i++){
14531                         if(keyCode[i] == k){
14532                           if(this.stopEvent){
14533                               e.stopEvent();
14534                           }
14535                           fn.call(scope || window, k, e);
14536                           return;
14537                         }
14538                     }
14539                 }else{
14540                     if(k == keyCode){
14541                         if(this.stopEvent){
14542                            e.stopEvent();
14543                         }
14544                         fn.call(scope || window, k, e);
14545                     }
14546                 }
14547             }
14548         };
14549         this.bindings.push(handler);  
14550         },
14551
14552     /**
14553      * Shorthand for adding a single key listener
14554      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14555      * following options:
14556      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14557      * @param {Function} fn The function to call
14558      * @param {Object} scope (optional) The scope of the function
14559      */
14560     on : function(key, fn, scope){
14561         var keyCode, shift, ctrl, alt;
14562         if(typeof key == "object" && !(key instanceof Array)){
14563             keyCode = key.key;
14564             shift = key.shift;
14565             ctrl = key.ctrl;
14566             alt = key.alt;
14567         }else{
14568             keyCode = key;
14569         }
14570         this.addBinding({
14571             key: keyCode,
14572             shift: shift,
14573             ctrl: ctrl,
14574             alt: alt,
14575             fn: fn,
14576             scope: scope
14577         })
14578     },
14579
14580     // private
14581     handleKeyDown : function(e){
14582             if(this.enabled){ //just in case
14583             var b = this.bindings;
14584             for(var i = 0, len = b.length; i < len; i++){
14585                 b[i].call(this, e);
14586             }
14587             }
14588         },
14589         
14590         /**
14591          * Returns true if this KeyMap is enabled
14592          * @return {Boolean} 
14593          */
14594         isEnabled : function(){
14595             return this.enabled;  
14596         },
14597         
14598         /**
14599          * Enables this KeyMap
14600          */
14601         enable: function(){
14602                 if(!this.enabled){
14603                     this.el.on(this.eventName, this.handleKeyDown, this);
14604                     this.enabled = true;
14605                 }
14606         },
14607
14608         /**
14609          * Disable this KeyMap
14610          */
14611         disable: function(){
14612                 if(this.enabled){
14613                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14614                     this.enabled = false;
14615                 }
14616         }
14617 };/*
14618  * Based on:
14619  * Ext JS Library 1.1.1
14620  * Copyright(c) 2006-2007, Ext JS, LLC.
14621  *
14622  * Originally Released Under LGPL - original licence link has changed is not relivant.
14623  *
14624  * Fork - LGPL
14625  * <script type="text/javascript">
14626  */
14627
14628  
14629 /**
14630  * @class Roo.util.TextMetrics
14631  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14632  * wide, in pixels, a given block of text will be.
14633  * @singleton
14634  */
14635 Roo.util.TextMetrics = function(){
14636     var shared;
14637     return {
14638         /**
14639          * Measures the size of the specified text
14640          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14641          * that can affect the size of the rendered text
14642          * @param {String} text The text to measure
14643          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14644          * in order to accurately measure the text height
14645          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14646          */
14647         measure : function(el, text, fixedWidth){
14648             if(!shared){
14649                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14650             }
14651             shared.bind(el);
14652             shared.setFixedWidth(fixedWidth || 'auto');
14653             return shared.getSize(text);
14654         },
14655
14656         /**
14657          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14658          * the overhead of multiple calls to initialize the style properties on each measurement.
14659          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14660          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14661          * in order to accurately measure the text height
14662          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14663          */
14664         createInstance : function(el, fixedWidth){
14665             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14666         }
14667     };
14668 }();
14669
14670  
14671
14672 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14673     var ml = new Roo.Element(document.createElement('div'));
14674     document.body.appendChild(ml.dom);
14675     ml.position('absolute');
14676     ml.setLeftTop(-1000, -1000);
14677     ml.hide();
14678
14679     if(fixedWidth){
14680         ml.setWidth(fixedWidth);
14681     }
14682      
14683     var instance = {
14684         /**
14685          * Returns the size of the specified text based on the internal element's style and width properties
14686          * @memberOf Roo.util.TextMetrics.Instance#
14687          * @param {String} text The text to measure
14688          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14689          */
14690         getSize : function(text){
14691             ml.update(text);
14692             var s = ml.getSize();
14693             ml.update('');
14694             return s;
14695         },
14696
14697         /**
14698          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14699          * that can affect the size of the rendered text
14700          * @memberOf Roo.util.TextMetrics.Instance#
14701          * @param {String/HTMLElement} el The element, dom node or id
14702          */
14703         bind : function(el){
14704             ml.setStyle(
14705                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14706             );
14707         },
14708
14709         /**
14710          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14711          * to set a fixed width in order to accurately measure the text height.
14712          * @memberOf Roo.util.TextMetrics.Instance#
14713          * @param {Number} width The width to set on the element
14714          */
14715         setFixedWidth : function(width){
14716             ml.setWidth(width);
14717         },
14718
14719         /**
14720          * Returns the measured width of the specified text
14721          * @memberOf Roo.util.TextMetrics.Instance#
14722          * @param {String} text The text to measure
14723          * @return {Number} width The width in pixels
14724          */
14725         getWidth : function(text){
14726             ml.dom.style.width = 'auto';
14727             return this.getSize(text).width;
14728         },
14729
14730         /**
14731          * Returns the measured height of the specified text.  For multiline text, be sure to call
14732          * {@link #setFixedWidth} if necessary.
14733          * @memberOf Roo.util.TextMetrics.Instance#
14734          * @param {String} text The text to measure
14735          * @return {Number} height The height in pixels
14736          */
14737         getHeight : function(text){
14738             return this.getSize(text).height;
14739         }
14740     };
14741
14742     instance.bind(bindTo);
14743
14744     return instance;
14745 };
14746
14747 // backwards compat
14748 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14749  * Based on:
14750  * Ext JS Library 1.1.1
14751  * Copyright(c) 2006-2007, Ext JS, LLC.
14752  *
14753  * Originally Released Under LGPL - original licence link has changed is not relivant.
14754  *
14755  * Fork - LGPL
14756  * <script type="text/javascript">
14757  */
14758
14759 /**
14760  * @class Roo.state.Provider
14761  * Abstract base class for state provider implementations. This class provides methods
14762  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14763  * Provider interface.
14764  */
14765 Roo.state.Provider = function(){
14766     /**
14767      * @event statechange
14768      * Fires when a state change occurs.
14769      * @param {Provider} this This state provider
14770      * @param {String} key The state key which was changed
14771      * @param {String} value The encoded value for the state
14772      */
14773     this.addEvents({
14774         "statechange": true
14775     });
14776     this.state = {};
14777     Roo.state.Provider.superclass.constructor.call(this);
14778 };
14779 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14780     /**
14781      * Returns the current value for a key
14782      * @param {String} name The key name
14783      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14784      * @return {Mixed} The state data
14785      */
14786     get : function(name, defaultValue){
14787         return typeof this.state[name] == "undefined" ?
14788             defaultValue : this.state[name];
14789     },
14790     
14791     /**
14792      * Clears a value from the state
14793      * @param {String} name The key name
14794      */
14795     clear : function(name){
14796         delete this.state[name];
14797         this.fireEvent("statechange", this, name, null);
14798     },
14799     
14800     /**
14801      * Sets the value for a key
14802      * @param {String} name The key name
14803      * @param {Mixed} value The value to set
14804      */
14805     set : function(name, value){
14806         this.state[name] = value;
14807         this.fireEvent("statechange", this, name, value);
14808     },
14809     
14810     /**
14811      * Decodes a string previously encoded with {@link #encodeValue}.
14812      * @param {String} value The value to decode
14813      * @return {Mixed} The decoded value
14814      */
14815     decodeValue : function(cookie){
14816         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14817         var matches = re.exec(unescape(cookie));
14818         if(!matches || !matches[1]) {
14819             return; // non state cookie
14820         }
14821         var type = matches[1];
14822         var v = matches[2];
14823         switch(type){
14824             case "n":
14825                 return parseFloat(v);
14826             case "d":
14827                 return new Date(Date.parse(v));
14828             case "b":
14829                 return (v == "1");
14830             case "a":
14831                 var all = [];
14832                 var values = v.split("^");
14833                 for(var i = 0, len = values.length; i < len; i++){
14834                     all.push(this.decodeValue(values[i]));
14835                 }
14836                 return all;
14837            case "o":
14838                 var all = {};
14839                 var values = v.split("^");
14840                 for(var i = 0, len = values.length; i < len; i++){
14841                     var kv = values[i].split("=");
14842                     all[kv[0]] = this.decodeValue(kv[1]);
14843                 }
14844                 return all;
14845            default:
14846                 return v;
14847         }
14848     },
14849     
14850     /**
14851      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14852      * @param {Mixed} value The value to encode
14853      * @return {String} The encoded value
14854      */
14855     encodeValue : function(v){
14856         var enc;
14857         if(typeof v == "number"){
14858             enc = "n:" + v;
14859         }else if(typeof v == "boolean"){
14860             enc = "b:" + (v ? "1" : "0");
14861         }else if(v instanceof Date){
14862             enc = "d:" + v.toGMTString();
14863         }else if(v instanceof Array){
14864             var flat = "";
14865             for(var i = 0, len = v.length; i < len; i++){
14866                 flat += this.encodeValue(v[i]);
14867                 if(i != len-1) {
14868                     flat += "^";
14869                 }
14870             }
14871             enc = "a:" + flat;
14872         }else if(typeof v == "object"){
14873             var flat = "";
14874             for(var key in v){
14875                 if(typeof v[key] != "function"){
14876                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14877                 }
14878             }
14879             enc = "o:" + flat.substring(0, flat.length-1);
14880         }else{
14881             enc = "s:" + v;
14882         }
14883         return escape(enc);        
14884     }
14885 });
14886
14887 /*
14888  * Based on:
14889  * Ext JS Library 1.1.1
14890  * Copyright(c) 2006-2007, Ext JS, LLC.
14891  *
14892  * Originally Released Under LGPL - original licence link has changed is not relivant.
14893  *
14894  * Fork - LGPL
14895  * <script type="text/javascript">
14896  */
14897 /**
14898  * @class Roo.state.Manager
14899  * This is the global state manager. By default all components that are "state aware" check this class
14900  * for state information if you don't pass them a custom state provider. In order for this class
14901  * to be useful, it must be initialized with a provider when your application initializes.
14902  <pre><code>
14903 // in your initialization function
14904 init : function(){
14905    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14906    ...
14907    // supposed you have a {@link Roo.BorderLayout}
14908    var layout = new Roo.BorderLayout(...);
14909    layout.restoreState();
14910    // or a {Roo.BasicDialog}
14911    var dialog = new Roo.BasicDialog(...);
14912    dialog.restoreState();
14913  </code></pre>
14914  * @singleton
14915  */
14916 Roo.state.Manager = function(){
14917     var provider = new Roo.state.Provider();
14918     
14919     return {
14920         /**
14921          * Configures the default state provider for your application
14922          * @param {Provider} stateProvider The state provider to set
14923          */
14924         setProvider : function(stateProvider){
14925             provider = stateProvider;
14926         },
14927         
14928         /**
14929          * Returns the current value for a key
14930          * @param {String} name The key name
14931          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14932          * @return {Mixed} The state data
14933          */
14934         get : function(key, defaultValue){
14935             return provider.get(key, defaultValue);
14936         },
14937         
14938         /**
14939          * Sets the value for a key
14940          * @param {String} name The key name
14941          * @param {Mixed} value The state data
14942          */
14943          set : function(key, value){
14944             provider.set(key, value);
14945         },
14946         
14947         /**
14948          * Clears a value from the state
14949          * @param {String} name The key name
14950          */
14951         clear : function(key){
14952             provider.clear(key);
14953         },
14954         
14955         /**
14956          * Gets the currently configured state provider
14957          * @return {Provider} The state provider
14958          */
14959         getProvider : function(){
14960             return provider;
14961         }
14962     };
14963 }();
14964 /*
14965  * Based on:
14966  * Ext JS Library 1.1.1
14967  * Copyright(c) 2006-2007, Ext JS, LLC.
14968  *
14969  * Originally Released Under LGPL - original licence link has changed is not relivant.
14970  *
14971  * Fork - LGPL
14972  * <script type="text/javascript">
14973  */
14974 /**
14975  * @class Roo.state.CookieProvider
14976  * @extends Roo.state.Provider
14977  * The default Provider implementation which saves state via cookies.
14978  * <br />Usage:
14979  <pre><code>
14980    var cp = new Roo.state.CookieProvider({
14981        path: "/cgi-bin/",
14982        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14983        domain: "roojs.com"
14984    })
14985    Roo.state.Manager.setProvider(cp);
14986  </code></pre>
14987  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14988  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14989  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14990  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14991  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14992  * domain the page is running on including the 'www' like 'www.roojs.com')
14993  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14994  * @constructor
14995  * Create a new CookieProvider
14996  * @param {Object} config The configuration object
14997  */
14998 Roo.state.CookieProvider = function(config){
14999     Roo.state.CookieProvider.superclass.constructor.call(this);
15000     this.path = "/";
15001     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15002     this.domain = null;
15003     this.secure = false;
15004     Roo.apply(this, config);
15005     this.state = this.readCookies();
15006 };
15007
15008 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15009     // private
15010     set : function(name, value){
15011         if(typeof value == "undefined" || value === null){
15012             this.clear(name);
15013             return;
15014         }
15015         this.setCookie(name, value);
15016         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15017     },
15018
15019     // private
15020     clear : function(name){
15021         this.clearCookie(name);
15022         Roo.state.CookieProvider.superclass.clear.call(this, name);
15023     },
15024
15025     // private
15026     readCookies : function(){
15027         var cookies = {};
15028         var c = document.cookie + ";";
15029         var re = /\s?(.*?)=(.*?);/g;
15030         var matches;
15031         while((matches = re.exec(c)) != null){
15032             var name = matches[1];
15033             var value = matches[2];
15034             if(name && name.substring(0,3) == "ys-"){
15035                 cookies[name.substr(3)] = this.decodeValue(value);
15036             }
15037         }
15038         return cookies;
15039     },
15040
15041     // private
15042     setCookie : function(name, value){
15043         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15044            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15045            ((this.path == null) ? "" : ("; path=" + this.path)) +
15046            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15047            ((this.secure == true) ? "; secure" : "");
15048     },
15049
15050     // private
15051     clearCookie : function(name){
15052         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15053            ((this.path == null) ? "" : ("; path=" + this.path)) +
15054            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15055            ((this.secure == true) ? "; secure" : "");
15056     }
15057 });/*
15058  * Based on:
15059  * Ext JS Library 1.1.1
15060  * Copyright(c) 2006-2007, Ext JS, LLC.
15061  *
15062  * Originally Released Under LGPL - original licence link has changed is not relivant.
15063  *
15064  * Fork - LGPL
15065  * <script type="text/javascript">
15066  */
15067  
15068
15069 /**
15070  * @class Roo.ComponentMgr
15071  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15072  * @singleton
15073  */
15074 Roo.ComponentMgr = function(){
15075     var all = new Roo.util.MixedCollection();
15076
15077     return {
15078         /**
15079          * Registers a component.
15080          * @param {Roo.Component} c The component
15081          */
15082         register : function(c){
15083             all.add(c);
15084         },
15085
15086         /**
15087          * Unregisters a component.
15088          * @param {Roo.Component} c The component
15089          */
15090         unregister : function(c){
15091             all.remove(c);
15092         },
15093
15094         /**
15095          * Returns a component by id
15096          * @param {String} id The component id
15097          */
15098         get : function(id){
15099             return all.get(id);
15100         },
15101
15102         /**
15103          * Registers a function that will be called when a specified component is added to ComponentMgr
15104          * @param {String} id The component id
15105          * @param {Funtction} fn The callback function
15106          * @param {Object} scope The scope of the callback
15107          */
15108         onAvailable : function(id, fn, scope){
15109             all.on("add", function(index, o){
15110                 if(o.id == id){
15111                     fn.call(scope || o, o);
15112                     all.un("add", fn, scope);
15113                 }
15114             });
15115         }
15116     };
15117 }();/*
15118  * Based on:
15119  * Ext JS Library 1.1.1
15120  * Copyright(c) 2006-2007, Ext JS, LLC.
15121  *
15122  * Originally Released Under LGPL - original licence link has changed is not relivant.
15123  *
15124  * Fork - LGPL
15125  * <script type="text/javascript">
15126  */
15127  
15128 /**
15129  * @class Roo.Component
15130  * @extends Roo.util.Observable
15131  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15132  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15133  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15134  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15135  * All visual components (widgets) that require rendering into a layout should subclass Component.
15136  * @constructor
15137  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15138  * 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
15139  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15140  */
15141 Roo.Component = function(config){
15142     config = config || {};
15143     if(config.tagName || config.dom || typeof config == "string"){ // element object
15144         config = {el: config, id: config.id || config};
15145     }
15146     this.initialConfig = config;
15147
15148     Roo.apply(this, config);
15149     this.addEvents({
15150         /**
15151          * @event disable
15152          * Fires after the component is disabled.
15153              * @param {Roo.Component} this
15154              */
15155         disable : true,
15156         /**
15157          * @event enable
15158          * Fires after the component is enabled.
15159              * @param {Roo.Component} this
15160              */
15161         enable : true,
15162         /**
15163          * @event beforeshow
15164          * Fires before the component is shown.  Return false to stop the show.
15165              * @param {Roo.Component} this
15166              */
15167         beforeshow : true,
15168         /**
15169          * @event show
15170          * Fires after the component is shown.
15171              * @param {Roo.Component} this
15172              */
15173         show : true,
15174         /**
15175          * @event beforehide
15176          * Fires before the component is hidden. Return false to stop the hide.
15177              * @param {Roo.Component} this
15178              */
15179         beforehide : true,
15180         /**
15181          * @event hide
15182          * Fires after the component is hidden.
15183              * @param {Roo.Component} this
15184              */
15185         hide : true,
15186         /**
15187          * @event beforerender
15188          * Fires before the component is rendered. Return false to stop the render.
15189              * @param {Roo.Component} this
15190              */
15191         beforerender : true,
15192         /**
15193          * @event render
15194          * Fires after the component is rendered.
15195              * @param {Roo.Component} this
15196              */
15197         render : true,
15198         /**
15199          * @event beforedestroy
15200          * Fires before the component is destroyed. Return false to stop the destroy.
15201              * @param {Roo.Component} this
15202              */
15203         beforedestroy : true,
15204         /**
15205          * @event destroy
15206          * Fires after the component is destroyed.
15207              * @param {Roo.Component} this
15208              */
15209         destroy : true
15210     });
15211     if(!this.id){
15212         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15213     }
15214     Roo.ComponentMgr.register(this);
15215     Roo.Component.superclass.constructor.call(this);
15216     this.initComponent();
15217     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15218         this.render(this.renderTo);
15219         delete this.renderTo;
15220     }
15221 };
15222
15223 /** @private */
15224 Roo.Component.AUTO_ID = 1000;
15225
15226 Roo.extend(Roo.Component, Roo.util.Observable, {
15227     /**
15228      * @scope Roo.Component.prototype
15229      * @type {Boolean}
15230      * true if this component is hidden. Read-only.
15231      */
15232     hidden : false,
15233     /**
15234      * @type {Boolean}
15235      * true if this component is disabled. Read-only.
15236      */
15237     disabled : false,
15238     /**
15239      * @type {Boolean}
15240      * true if this component has been rendered. Read-only.
15241      */
15242     rendered : false,
15243     
15244     /** @cfg {String} disableClass
15245      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15246      */
15247     disabledClass : "x-item-disabled",
15248         /** @cfg {Boolean} allowDomMove
15249          * Whether the component can move the Dom node when rendering (defaults to true).
15250          */
15251     allowDomMove : true,
15252     /** @cfg {String} hideMode (display|visibility)
15253      * How this component should hidden. Supported values are
15254      * "visibility" (css visibility), "offsets" (negative offset position) and
15255      * "display" (css display) - defaults to "display".
15256      */
15257     hideMode: 'display',
15258
15259     /** @private */
15260     ctype : "Roo.Component",
15261
15262     /**
15263      * @cfg {String} actionMode 
15264      * which property holds the element that used for  hide() / show() / disable() / enable()
15265      * default is 'el' 
15266      */
15267     actionMode : "el",
15268
15269     /** @private */
15270     getActionEl : function(){
15271         return this[this.actionMode];
15272     },
15273
15274     initComponent : Roo.emptyFn,
15275     /**
15276      * If this is a lazy rendering component, render it to its container element.
15277      * @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.
15278      */
15279     render : function(container, position){
15280         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15281             if(!container && this.el){
15282                 this.el = Roo.get(this.el);
15283                 container = this.el.dom.parentNode;
15284                 this.allowDomMove = false;
15285             }
15286             this.container = Roo.get(container);
15287             this.rendered = true;
15288             if(position !== undefined){
15289                 if(typeof position == 'number'){
15290                     position = this.container.dom.childNodes[position];
15291                 }else{
15292                     position = Roo.getDom(position);
15293                 }
15294             }
15295             this.onRender(this.container, position || null);
15296             if(this.cls){
15297                 this.el.addClass(this.cls);
15298                 delete this.cls;
15299             }
15300             if(this.style){
15301                 this.el.applyStyles(this.style);
15302                 delete this.style;
15303             }
15304             this.fireEvent("render", this);
15305             this.afterRender(this.container);
15306             if(this.hidden){
15307                 this.hide();
15308             }
15309             if(this.disabled){
15310                 this.disable();
15311             }
15312         }
15313         return this;
15314     },
15315
15316     /** @private */
15317     // default function is not really useful
15318     onRender : function(ct, position){
15319         if(this.el){
15320             this.el = Roo.get(this.el);
15321             if(this.allowDomMove !== false){
15322                 ct.dom.insertBefore(this.el.dom, position);
15323             }
15324         }
15325     },
15326
15327     /** @private */
15328     getAutoCreate : function(){
15329         var cfg = typeof this.autoCreate == "object" ?
15330                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15331         if(this.id && !cfg.id){
15332             cfg.id = this.id;
15333         }
15334         return cfg;
15335     },
15336
15337     /** @private */
15338     afterRender : Roo.emptyFn,
15339
15340     /**
15341      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15342      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15343      */
15344     destroy : function(){
15345         if(this.fireEvent("beforedestroy", this) !== false){
15346             this.purgeListeners();
15347             this.beforeDestroy();
15348             if(this.rendered){
15349                 this.el.removeAllListeners();
15350                 this.el.remove();
15351                 if(this.actionMode == "container"){
15352                     this.container.remove();
15353                 }
15354             }
15355             this.onDestroy();
15356             Roo.ComponentMgr.unregister(this);
15357             this.fireEvent("destroy", this);
15358         }
15359     },
15360
15361         /** @private */
15362     beforeDestroy : function(){
15363
15364     },
15365
15366         /** @private */
15367         onDestroy : function(){
15368
15369     },
15370
15371     /**
15372      * Returns the underlying {@link Roo.Element}.
15373      * @return {Roo.Element} The element
15374      */
15375     getEl : function(){
15376         return this.el;
15377     },
15378
15379     /**
15380      * Returns the id of this component.
15381      * @return {String}
15382      */
15383     getId : function(){
15384         return this.id;
15385     },
15386
15387     /**
15388      * Try to focus this component.
15389      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15390      * @return {Roo.Component} this
15391      */
15392     focus : function(selectText){
15393         if(this.rendered){
15394             this.el.focus();
15395             if(selectText === true){
15396                 this.el.dom.select();
15397             }
15398         }
15399         return this;
15400     },
15401
15402     /** @private */
15403     blur : function(){
15404         if(this.rendered){
15405             this.el.blur();
15406         }
15407         return this;
15408     },
15409
15410     /**
15411      * Disable this component.
15412      * @return {Roo.Component} this
15413      */
15414     disable : function(){
15415         if(this.rendered){
15416             this.onDisable();
15417         }
15418         this.disabled = true;
15419         this.fireEvent("disable", this);
15420         return this;
15421     },
15422
15423         // private
15424     onDisable : function(){
15425         this.getActionEl().addClass(this.disabledClass);
15426         this.el.dom.disabled = true;
15427     },
15428
15429     /**
15430      * Enable this component.
15431      * @return {Roo.Component} this
15432      */
15433     enable : function(){
15434         if(this.rendered){
15435             this.onEnable();
15436         }
15437         this.disabled = false;
15438         this.fireEvent("enable", this);
15439         return this;
15440     },
15441
15442         // private
15443     onEnable : function(){
15444         this.getActionEl().removeClass(this.disabledClass);
15445         this.el.dom.disabled = false;
15446     },
15447
15448     /**
15449      * Convenience function for setting disabled/enabled by boolean.
15450      * @param {Boolean} disabled
15451      */
15452     setDisabled : function(disabled){
15453         this[disabled ? "disable" : "enable"]();
15454     },
15455
15456     /**
15457      * Show this component.
15458      * @return {Roo.Component} this
15459      */
15460     show: function(){
15461         if(this.fireEvent("beforeshow", this) !== false){
15462             this.hidden = false;
15463             if(this.rendered){
15464                 this.onShow();
15465             }
15466             this.fireEvent("show", this);
15467         }
15468         return this;
15469     },
15470
15471     // private
15472     onShow : function(){
15473         var ae = this.getActionEl();
15474         if(this.hideMode == 'visibility'){
15475             ae.dom.style.visibility = "visible";
15476         }else if(this.hideMode == 'offsets'){
15477             ae.removeClass('x-hidden');
15478         }else{
15479             ae.dom.style.display = "";
15480         }
15481     },
15482
15483     /**
15484      * Hide this component.
15485      * @return {Roo.Component} this
15486      */
15487     hide: function(){
15488         if(this.fireEvent("beforehide", this) !== false){
15489             this.hidden = true;
15490             if(this.rendered){
15491                 this.onHide();
15492             }
15493             this.fireEvent("hide", this);
15494         }
15495         return this;
15496     },
15497
15498     // private
15499     onHide : function(){
15500         var ae = this.getActionEl();
15501         if(this.hideMode == 'visibility'){
15502             ae.dom.style.visibility = "hidden";
15503         }else if(this.hideMode == 'offsets'){
15504             ae.addClass('x-hidden');
15505         }else{
15506             ae.dom.style.display = "none";
15507         }
15508     },
15509
15510     /**
15511      * Convenience function to hide or show this component by boolean.
15512      * @param {Boolean} visible True to show, false to hide
15513      * @return {Roo.Component} this
15514      */
15515     setVisible: function(visible){
15516         if(visible) {
15517             this.show();
15518         }else{
15519             this.hide();
15520         }
15521         return this;
15522     },
15523
15524     /**
15525      * Returns true if this component is visible.
15526      */
15527     isVisible : function(){
15528         return this.getActionEl().isVisible();
15529     },
15530
15531     cloneConfig : function(overrides){
15532         overrides = overrides || {};
15533         var id = overrides.id || Roo.id();
15534         var cfg = Roo.applyIf(overrides, this.initialConfig);
15535         cfg.id = id; // prevent dup id
15536         return new this.constructor(cfg);
15537     }
15538 });/*
15539  * Based on:
15540  * Ext JS Library 1.1.1
15541  * Copyright(c) 2006-2007, Ext JS, LLC.
15542  *
15543  * Originally Released Under LGPL - original licence link has changed is not relivant.
15544  *
15545  * Fork - LGPL
15546  * <script type="text/javascript">
15547  */
15548
15549 /**
15550  * @class Roo.BoxComponent
15551  * @extends Roo.Component
15552  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15553  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15554  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15555  * layout containers.
15556  * @constructor
15557  * @param {Roo.Element/String/Object} config The configuration options.
15558  */
15559 Roo.BoxComponent = function(config){
15560     Roo.Component.call(this, config);
15561     this.addEvents({
15562         /**
15563          * @event resize
15564          * Fires after the component is resized.
15565              * @param {Roo.Component} this
15566              * @param {Number} adjWidth The box-adjusted width that was set
15567              * @param {Number} adjHeight The box-adjusted height that was set
15568              * @param {Number} rawWidth The width that was originally specified
15569              * @param {Number} rawHeight The height that was originally specified
15570              */
15571         resize : true,
15572         /**
15573          * @event move
15574          * Fires after the component is moved.
15575              * @param {Roo.Component} this
15576              * @param {Number} x The new x position
15577              * @param {Number} y The new y position
15578              */
15579         move : true
15580     });
15581 };
15582
15583 Roo.extend(Roo.BoxComponent, Roo.Component, {
15584     // private, set in afterRender to signify that the component has been rendered
15585     boxReady : false,
15586     // private, used to defer height settings to subclasses
15587     deferHeight: false,
15588     /** @cfg {Number} width
15589      * width (optional) size of component
15590      */
15591      /** @cfg {Number} height
15592      * height (optional) size of component
15593      */
15594      
15595     /**
15596      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15597      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15598      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15599      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15600      * @return {Roo.BoxComponent} this
15601      */
15602     setSize : function(w, h){
15603         // support for standard size objects
15604         if(typeof w == 'object'){
15605             h = w.height;
15606             w = w.width;
15607         }
15608         // not rendered
15609         if(!this.boxReady){
15610             this.width = w;
15611             this.height = h;
15612             return this;
15613         }
15614
15615         // prevent recalcs when not needed
15616         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15617             return this;
15618         }
15619         this.lastSize = {width: w, height: h};
15620
15621         var adj = this.adjustSize(w, h);
15622         var aw = adj.width, ah = adj.height;
15623         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15624             var rz = this.getResizeEl();
15625             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15626                 rz.setSize(aw, ah);
15627             }else if(!this.deferHeight && ah !== undefined){
15628                 rz.setHeight(ah);
15629             }else if(aw !== undefined){
15630                 rz.setWidth(aw);
15631             }
15632             this.onResize(aw, ah, w, h);
15633             this.fireEvent('resize', this, aw, ah, w, h);
15634         }
15635         return this;
15636     },
15637
15638     /**
15639      * Gets the current size of the component's underlying element.
15640      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15641      */
15642     getSize : function(){
15643         return this.el.getSize();
15644     },
15645
15646     /**
15647      * Gets the current XY position of the component's underlying element.
15648      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15649      * @return {Array} The XY position of the element (e.g., [100, 200])
15650      */
15651     getPosition : function(local){
15652         if(local === true){
15653             return [this.el.getLeft(true), this.el.getTop(true)];
15654         }
15655         return this.xy || this.el.getXY();
15656     },
15657
15658     /**
15659      * Gets the current box measurements of the component's underlying element.
15660      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15661      * @returns {Object} box An object in the format {x, y, width, height}
15662      */
15663     getBox : function(local){
15664         var s = this.el.getSize();
15665         if(local){
15666             s.x = this.el.getLeft(true);
15667             s.y = this.el.getTop(true);
15668         }else{
15669             var xy = this.xy || this.el.getXY();
15670             s.x = xy[0];
15671             s.y = xy[1];
15672         }
15673         return s;
15674     },
15675
15676     /**
15677      * Sets the current box measurements of the component's underlying element.
15678      * @param {Object} box An object in the format {x, y, width, height}
15679      * @returns {Roo.BoxComponent} this
15680      */
15681     updateBox : function(box){
15682         this.setSize(box.width, box.height);
15683         this.setPagePosition(box.x, box.y);
15684         return this;
15685     },
15686
15687     // protected
15688     getResizeEl : function(){
15689         return this.resizeEl || this.el;
15690     },
15691
15692     // protected
15693     getPositionEl : function(){
15694         return this.positionEl || this.el;
15695     },
15696
15697     /**
15698      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15699      * This method fires the move event.
15700      * @param {Number} left The new left
15701      * @param {Number} top The new top
15702      * @returns {Roo.BoxComponent} this
15703      */
15704     setPosition : function(x, y){
15705         this.x = x;
15706         this.y = y;
15707         if(!this.boxReady){
15708             return this;
15709         }
15710         var adj = this.adjustPosition(x, y);
15711         var ax = adj.x, ay = adj.y;
15712
15713         var el = this.getPositionEl();
15714         if(ax !== undefined || ay !== undefined){
15715             if(ax !== undefined && ay !== undefined){
15716                 el.setLeftTop(ax, ay);
15717             }else if(ax !== undefined){
15718                 el.setLeft(ax);
15719             }else if(ay !== undefined){
15720                 el.setTop(ay);
15721             }
15722             this.onPosition(ax, ay);
15723             this.fireEvent('move', this, ax, ay);
15724         }
15725         return this;
15726     },
15727
15728     /**
15729      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15730      * This method fires the move event.
15731      * @param {Number} x The new x position
15732      * @param {Number} y The new y position
15733      * @returns {Roo.BoxComponent} this
15734      */
15735     setPagePosition : function(x, y){
15736         this.pageX = x;
15737         this.pageY = y;
15738         if(!this.boxReady){
15739             return;
15740         }
15741         if(x === undefined || y === undefined){ // cannot translate undefined points
15742             return;
15743         }
15744         var p = this.el.translatePoints(x, y);
15745         this.setPosition(p.left, p.top);
15746         return this;
15747     },
15748
15749     // private
15750     onRender : function(ct, position){
15751         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15752         if(this.resizeEl){
15753             this.resizeEl = Roo.get(this.resizeEl);
15754         }
15755         if(this.positionEl){
15756             this.positionEl = Roo.get(this.positionEl);
15757         }
15758     },
15759
15760     // private
15761     afterRender : function(){
15762         Roo.BoxComponent.superclass.afterRender.call(this);
15763         this.boxReady = true;
15764         this.setSize(this.width, this.height);
15765         if(this.x || this.y){
15766             this.setPosition(this.x, this.y);
15767         }
15768         if(this.pageX || this.pageY){
15769             this.setPagePosition(this.pageX, this.pageY);
15770         }
15771     },
15772
15773     /**
15774      * Force the component's size to recalculate based on the underlying element's current height and width.
15775      * @returns {Roo.BoxComponent} this
15776      */
15777     syncSize : function(){
15778         delete this.lastSize;
15779         this.setSize(this.el.getWidth(), this.el.getHeight());
15780         return this;
15781     },
15782
15783     /**
15784      * Called after the component is resized, this method is empty by default but can be implemented by any
15785      * subclass that needs to perform custom logic after a resize occurs.
15786      * @param {Number} adjWidth The box-adjusted width that was set
15787      * @param {Number} adjHeight The box-adjusted height that was set
15788      * @param {Number} rawWidth The width that was originally specified
15789      * @param {Number} rawHeight The height that was originally specified
15790      */
15791     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15792
15793     },
15794
15795     /**
15796      * Called after the component is moved, this method is empty by default but can be implemented by any
15797      * subclass that needs to perform custom logic after a move occurs.
15798      * @param {Number} x The new x position
15799      * @param {Number} y The new y position
15800      */
15801     onPosition : function(x, y){
15802
15803     },
15804
15805     // private
15806     adjustSize : function(w, h){
15807         if(this.autoWidth){
15808             w = 'auto';
15809         }
15810         if(this.autoHeight){
15811             h = 'auto';
15812         }
15813         return {width : w, height: h};
15814     },
15815
15816     // private
15817     adjustPosition : function(x, y){
15818         return {x : x, y: y};
15819     }
15820 });/*
15821  * Original code for Roojs - LGPL
15822  * <script type="text/javascript">
15823  */
15824  
15825 /**
15826  * @class Roo.XComponent
15827  * A delayed Element creator...
15828  * Or a way to group chunks of interface together.
15829  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15830  *  used in conjunction with XComponent.build() it will create an instance of each element,
15831  *  then call addxtype() to build the User interface.
15832  * 
15833  * Mypart.xyx = new Roo.XComponent({
15834
15835     parent : 'Mypart.xyz', // empty == document.element.!!
15836     order : '001',
15837     name : 'xxxx'
15838     region : 'xxxx'
15839     disabled : function() {} 
15840      
15841     tree : function() { // return an tree of xtype declared components
15842         var MODULE = this;
15843         return 
15844         {
15845             xtype : 'NestedLayoutPanel',
15846             // technicall
15847         }
15848      ]
15849  *})
15850  *
15851  *
15852  * It can be used to build a big heiracy, with parent etc.
15853  * or you can just use this to render a single compoent to a dom element
15854  * MYPART.render(Roo.Element | String(id) | dom_element )
15855  *
15856  *
15857  * Usage patterns.
15858  *
15859  * Classic Roo
15860  *
15861  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15862  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15863  *
15864  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15865  *
15866  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15867  * - if mulitple topModules exist, the last one is defined as the top module.
15868  *
15869  * Embeded Roo
15870  * 
15871  * When the top level or multiple modules are to embedded into a existing HTML page,
15872  * the parent element can container '#id' of the element where the module will be drawn.
15873  *
15874  * Bootstrap Roo
15875  *
15876  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15877  * it relies more on a include mechanism, where sub modules are included into an outer page.
15878  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15879  * 
15880  * Bootstrap Roo Included elements
15881  *
15882  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15883  * hence confusing the component builder as it thinks there are multiple top level elements. 
15884  *
15885  * 
15886  * 
15887  * @extends Roo.util.Observable
15888  * @constructor
15889  * @param cfg {Object} configuration of component
15890  * 
15891  */
15892 Roo.XComponent = function(cfg) {
15893     Roo.apply(this, cfg);
15894     this.addEvents({ 
15895         /**
15896              * @event built
15897              * Fires when this the componnt is built
15898              * @param {Roo.XComponent} c the component
15899              */
15900         'built' : true
15901         
15902     });
15903     this.region = this.region || 'center'; // default..
15904     Roo.XComponent.register(this);
15905     this.modules = false;
15906     this.el = false; // where the layout goes..
15907     
15908     
15909 }
15910 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15911     /**
15912      * @property el
15913      * The created element (with Roo.factory())
15914      * @type {Roo.Layout}
15915      */
15916     el  : false,
15917     
15918     /**
15919      * @property el
15920      * for BC  - use el in new code
15921      * @type {Roo.Layout}
15922      */
15923     panel : false,
15924     
15925     /**
15926      * @property layout
15927      * for BC  - use el in new code
15928      * @type {Roo.Layout}
15929      */
15930     layout : false,
15931     
15932      /**
15933      * @cfg {Function|boolean} disabled
15934      * If this module is disabled by some rule, return true from the funtion
15935      */
15936     disabled : false,
15937     
15938     /**
15939      * @cfg {String} parent 
15940      * Name of parent element which it get xtype added to..
15941      */
15942     parent: false,
15943     
15944     /**
15945      * @cfg {String} order
15946      * Used to set the order in which elements are created (usefull for multiple tabs)
15947      */
15948     
15949     order : false,
15950     /**
15951      * @cfg {String} name
15952      * String to display while loading.
15953      */
15954     name : false,
15955     /**
15956      * @cfg {String} region
15957      * Region to render component to (defaults to center)
15958      */
15959     region : 'center',
15960     
15961     /**
15962      * @cfg {Array} items
15963      * A single item array - the first element is the root of the tree..
15964      * It's done this way to stay compatible with the Xtype system...
15965      */
15966     items : false,
15967     
15968     /**
15969      * @property _tree
15970      * The method that retuns the tree of parts that make up this compoennt 
15971      * @type {function}
15972      */
15973     _tree  : false,
15974     
15975      /**
15976      * render
15977      * render element to dom or tree
15978      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15979      */
15980     
15981     render : function(el)
15982     {
15983         
15984         el = el || false;
15985         var hp = this.parent ? 1 : 0;
15986         Roo.debug &&  Roo.log(this);
15987         
15988         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15989             // if parent is a '#.....' string, then let's use that..
15990             var ename = this.parent.substr(1);
15991             this.parent = false;
15992             Roo.debug && Roo.log(ename);
15993             switch (ename) {
15994                 case 'bootstrap-body' :
15995                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15996                         this.parent = { el :  new  Roo.bootstrap.Body() };
15997                         Roo.debug && Roo.log("setting el to doc body");
15998                          
15999                     } else {
16000                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16001                     }
16002                     break;
16003                 case 'bootstrap':
16004                     this.parent = { el : true};
16005                     // fall through
16006                 default:
16007                     el = Roo.get(ename);
16008                     break;
16009             }
16010                 
16011             
16012             if (!el && !this.parent) {
16013                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16014                 return;
16015             }
16016         }
16017         Roo.debug && Roo.log("EL:");
16018         Roo.debug && Roo.log(el);
16019         Roo.debug && Roo.log("this.parent.el:");
16020         Roo.debug && Roo.log(this.parent.el);
16021         
16022         var tree = this._tree ? this._tree() : this.tree();
16023
16024         // altertive root elements ??? - we need a better way to indicate these.
16025         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16026                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16027         
16028         if (!this.parent && is_alt) {
16029             //el = Roo.get(document.body);
16030             this.parent = { el : true };
16031         }
16032             
16033             
16034         
16035         if (!this.parent) {
16036             
16037             Roo.debug && Roo.log("no parent - creating one");
16038             
16039             el = el ? Roo.get(el) : false;      
16040             
16041             // it's a top level one..
16042             this.parent =  {
16043                 el : new Roo.BorderLayout(el || document.body, {
16044                 
16045                      center: {
16046                          titlebar: false,
16047                          autoScroll:false,
16048                          closeOnTab: true,
16049                          tabPosition: 'top',
16050                           //resizeTabs: true,
16051                          alwaysShowTabs: el && hp? false :  true,
16052                          hideTabs: el || !hp ? true :  false,
16053                          minTabWidth: 140
16054                      }
16055                  })
16056             }
16057         }
16058         
16059         if (!this.parent.el) {
16060                 // probably an old style ctor, which has been disabled.
16061                 return;
16062
16063         }
16064                 // The 'tree' method is  '_tree now' 
16065             
16066         tree.region = tree.region || this.region;
16067         
16068         if (this.parent.el === true) {
16069             // bootstrap... - body..
16070             this.parent.el = Roo.factory(tree);
16071         }
16072         
16073         this.el = this.parent.el.addxtype(tree);
16074         this.fireEvent('built', this);
16075         
16076         this.panel = this.el;
16077         this.layout = this.panel.layout;
16078         this.parentLayout = this.parent.layout  || false;  
16079          
16080     }
16081     
16082 });
16083
16084 Roo.apply(Roo.XComponent, {
16085     /**
16086      * @property  hideProgress
16087      * true to disable the building progress bar.. usefull on single page renders.
16088      * @type Boolean
16089      */
16090     hideProgress : false,
16091     /**
16092      * @property  buildCompleted
16093      * True when the builder has completed building the interface.
16094      * @type Boolean
16095      */
16096     buildCompleted : false,
16097      
16098     /**
16099      * @property  topModule
16100      * the upper most module - uses document.element as it's constructor.
16101      * @type Object
16102      */
16103      
16104     topModule  : false,
16105       
16106     /**
16107      * @property  modules
16108      * array of modules to be created by registration system.
16109      * @type {Array} of Roo.XComponent
16110      */
16111     
16112     modules : [],
16113     /**
16114      * @property  elmodules
16115      * array of modules to be created by which use #ID 
16116      * @type {Array} of Roo.XComponent
16117      */
16118      
16119     elmodules : [],
16120
16121      /**
16122      * @property  build_from_html
16123      * Build elements from html - used by bootstrap HTML stuff 
16124      *    - this is cleared after build is completed
16125      * @type {boolean} true  (default false)
16126      */
16127      
16128     build_from_html : false,
16129
16130     /**
16131      * Register components to be built later.
16132      *
16133      * This solves the following issues
16134      * - Building is not done on page load, but after an authentication process has occured.
16135      * - Interface elements are registered on page load
16136      * - Parent Interface elements may not be loaded before child, so this handles that..
16137      * 
16138      *
16139      * example:
16140      * 
16141      * MyApp.register({
16142           order : '000001',
16143           module : 'Pman.Tab.projectMgr',
16144           region : 'center',
16145           parent : 'Pman.layout',
16146           disabled : false,  // or use a function..
16147         })
16148      
16149      * * @param {Object} details about module
16150      */
16151     register : function(obj) {
16152                 
16153         Roo.XComponent.event.fireEvent('register', obj);
16154         switch(typeof(obj.disabled) ) {
16155                 
16156             case 'undefined':
16157                 break;
16158             
16159             case 'function':
16160                 if ( obj.disabled() ) {
16161                         return;
16162                 }
16163                 break;
16164             
16165             default:
16166                 if (obj.disabled) {
16167                         return;
16168                 }
16169                 break;
16170         }
16171                 
16172         this.modules.push(obj);
16173          
16174     },
16175     /**
16176      * convert a string to an object..
16177      * eg. 'AAA.BBB' -> finds AAA.BBB
16178
16179      */
16180     
16181     toObject : function(str)
16182     {
16183         if (!str || typeof(str) == 'object') {
16184             return str;
16185         }
16186         if (str.substring(0,1) == '#') {
16187             return str;
16188         }
16189
16190         var ar = str.split('.');
16191         var rt, o;
16192         rt = ar.shift();
16193             /** eval:var:o */
16194         try {
16195             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16196         } catch (e) {
16197             throw "Module not found : " + str;
16198         }
16199         
16200         if (o === false) {
16201             throw "Module not found : " + str;
16202         }
16203         Roo.each(ar, function(e) {
16204             if (typeof(o[e]) == 'undefined') {
16205                 throw "Module not found : " + str;
16206             }
16207             o = o[e];
16208         });
16209         
16210         return o;
16211         
16212     },
16213     
16214     
16215     /**
16216      * move modules into their correct place in the tree..
16217      * 
16218      */
16219     preBuild : function ()
16220     {
16221         var _t = this;
16222         Roo.each(this.modules , function (obj)
16223         {
16224             Roo.XComponent.event.fireEvent('beforebuild', obj);
16225             
16226             var opar = obj.parent;
16227             try { 
16228                 obj.parent = this.toObject(opar);
16229             } catch(e) {
16230                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16231                 return;
16232             }
16233             
16234             if (!obj.parent) {
16235                 Roo.debug && Roo.log("GOT top level module");
16236                 Roo.debug && Roo.log(obj);
16237                 obj.modules = new Roo.util.MixedCollection(false, 
16238                     function(o) { return o.order + '' }
16239                 );
16240                 this.topModule = obj;
16241                 return;
16242             }
16243                         // parent is a string (usually a dom element name..)
16244             if (typeof(obj.parent) == 'string') {
16245                 this.elmodules.push(obj);
16246                 return;
16247             }
16248             if (obj.parent.constructor != Roo.XComponent) {
16249                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16250             }
16251             if (!obj.parent.modules) {
16252                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16253                     function(o) { return o.order + '' }
16254                 );
16255             }
16256             if (obj.parent.disabled) {
16257                 obj.disabled = true;
16258             }
16259             obj.parent.modules.add(obj);
16260         }, this);
16261     },
16262     
16263      /**
16264      * make a list of modules to build.
16265      * @return {Array} list of modules. 
16266      */ 
16267     
16268     buildOrder : function()
16269     {
16270         var _this = this;
16271         var cmp = function(a,b) {   
16272             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16273         };
16274         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16275             throw "No top level modules to build";
16276         }
16277         
16278         // make a flat list in order of modules to build.
16279         var mods = this.topModule ? [ this.topModule ] : [];
16280                 
16281         
16282         // elmodules (is a list of DOM based modules )
16283         Roo.each(this.elmodules, function(e) {
16284             mods.push(e);
16285             if (!this.topModule &&
16286                 typeof(e.parent) == 'string' &&
16287                 e.parent.substring(0,1) == '#' &&
16288                 Roo.get(e.parent.substr(1))
16289                ) {
16290                 
16291                 _this.topModule = e;
16292             }
16293             
16294         });
16295
16296         
16297         // add modules to their parents..
16298         var addMod = function(m) {
16299             Roo.debug && Roo.log("build Order: add: " + m.name);
16300                 
16301             mods.push(m);
16302             if (m.modules && !m.disabled) {
16303                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16304                 m.modules.keySort('ASC',  cmp );
16305                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16306     
16307                 m.modules.each(addMod);
16308             } else {
16309                 Roo.debug && Roo.log("build Order: no child modules");
16310             }
16311             // not sure if this is used any more..
16312             if (m.finalize) {
16313                 m.finalize.name = m.name + " (clean up) ";
16314                 mods.push(m.finalize);
16315             }
16316             
16317         }
16318         if (this.topModule && this.topModule.modules) { 
16319             this.topModule.modules.keySort('ASC',  cmp );
16320             this.topModule.modules.each(addMod);
16321         } 
16322         return mods;
16323     },
16324     
16325      /**
16326      * Build the registered modules.
16327      * @param {Object} parent element.
16328      * @param {Function} optional method to call after module has been added.
16329      * 
16330      */ 
16331    
16332     build : function(opts) 
16333     {
16334         
16335         if (typeof(opts) != 'undefined') {
16336             Roo.apply(this,opts);
16337         }
16338         
16339         this.preBuild();
16340         var mods = this.buildOrder();
16341       
16342         //this.allmods = mods;
16343         //Roo.debug && Roo.log(mods);
16344         //return;
16345         if (!mods.length) { // should not happen
16346             throw "NO modules!!!";
16347         }
16348         
16349         
16350         var msg = "Building Interface...";
16351         // flash it up as modal - so we store the mask!?
16352         if (!this.hideProgress && Roo.MessageBox) {
16353             Roo.MessageBox.show({ title: 'loading' });
16354             Roo.MessageBox.show({
16355                title: "Please wait...",
16356                msg: msg,
16357                width:450,
16358                progress:true,
16359                closable:false,
16360                modal: false
16361               
16362             });
16363         }
16364         var total = mods.length;
16365         
16366         var _this = this;
16367         var progressRun = function() {
16368             if (!mods.length) {
16369                 Roo.debug && Roo.log('hide?');
16370                 if (!this.hideProgress && Roo.MessageBox) {
16371                     Roo.MessageBox.hide();
16372                 }
16373                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16374                 
16375                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16376                 
16377                 // THE END...
16378                 return false;   
16379             }
16380             
16381             var m = mods.shift();
16382             
16383             
16384             Roo.debug && Roo.log(m);
16385             // not sure if this is supported any more.. - modules that are are just function
16386             if (typeof(m) == 'function') { 
16387                 m.call(this);
16388                 return progressRun.defer(10, _this);
16389             } 
16390             
16391             
16392             msg = "Building Interface " + (total  - mods.length) + 
16393                     " of " + total + 
16394                     (m.name ? (' - ' + m.name) : '');
16395                         Roo.debug && Roo.log(msg);
16396             if (!this.hideProgress &&  Roo.MessageBox) { 
16397                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16398             }
16399             
16400          
16401             // is the module disabled?
16402             var disabled = (typeof(m.disabled) == 'function') ?
16403                 m.disabled.call(m.module.disabled) : m.disabled;    
16404             
16405             
16406             if (disabled) {
16407                 return progressRun(); // we do not update the display!
16408             }
16409             
16410             // now build 
16411             
16412                         
16413                         
16414             m.render();
16415             // it's 10 on top level, and 1 on others??? why...
16416             return progressRun.defer(10, _this);
16417              
16418         }
16419         progressRun.defer(1, _this);
16420      
16421         
16422         
16423     },
16424         
16425         
16426         /**
16427          * Event Object.
16428          *
16429          *
16430          */
16431         event: false, 
16432     /**
16433          * wrapper for event.on - aliased later..  
16434          * Typically use to register a event handler for register:
16435          *
16436          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16437          *
16438          */
16439     on : false
16440    
16441     
16442     
16443 });
16444
16445 Roo.XComponent.event = new Roo.util.Observable({
16446                 events : { 
16447                         /**
16448                          * @event register
16449                          * Fires when an Component is registered,
16450                          * set the disable property on the Component to stop registration.
16451                          * @param {Roo.XComponent} c the component being registerd.
16452                          * 
16453                          */
16454                         'register' : true,
16455             /**
16456                          * @event beforebuild
16457                          * Fires before each Component is built
16458                          * can be used to apply permissions.
16459                          * @param {Roo.XComponent} c the component being registerd.
16460                          * 
16461                          */
16462                         'beforebuild' : true,
16463                         /**
16464                          * @event buildcomplete
16465                          * Fires on the top level element when all elements have been built
16466                          * @param {Roo.XComponent} the top level component.
16467                          */
16468                         'buildcomplete' : true
16469                         
16470                 }
16471 });
16472
16473 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16474  /*
16475  * Based on:
16476  * Ext JS Library 1.1.1
16477  * Copyright(c) 2006-2007, Ext JS, LLC.
16478  *
16479  * Originally Released Under LGPL - original licence link has changed is not relivant.
16480  *
16481  * Fork - LGPL
16482  * <script type="text/javascript">
16483  */
16484
16485
16486
16487 /*
16488  * These classes are derivatives of the similarly named classes in the YUI Library.
16489  * The original license:
16490  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16491  * Code licensed under the BSD License:
16492  * http://developer.yahoo.net/yui/license.txt
16493  */
16494
16495 (function() {
16496
16497 var Event=Roo.EventManager;
16498 var Dom=Roo.lib.Dom;
16499
16500 /**
16501  * @class Roo.dd.DragDrop
16502  * @extends Roo.util.Observable
16503  * Defines the interface and base operation of items that that can be
16504  * dragged or can be drop targets.  It was designed to be extended, overriding
16505  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16506  * Up to three html elements can be associated with a DragDrop instance:
16507  * <ul>
16508  * <li>linked element: the element that is passed into the constructor.
16509  * This is the element which defines the boundaries for interaction with
16510  * other DragDrop objects.</li>
16511  * <li>handle element(s): The drag operation only occurs if the element that
16512  * was clicked matches a handle element.  By default this is the linked
16513  * element, but there are times that you will want only a portion of the
16514  * linked element to initiate the drag operation, and the setHandleElId()
16515  * method provides a way to define this.</li>
16516  * <li>drag element: this represents the element that would be moved along
16517  * with the cursor during a drag operation.  By default, this is the linked
16518  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16519  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16520  * </li>
16521  * </ul>
16522  * This class should not be instantiated until the onload event to ensure that
16523  * the associated elements are available.
16524  * The following would define a DragDrop obj that would interact with any
16525  * other DragDrop obj in the "group1" group:
16526  * <pre>
16527  *  dd = new Roo.dd.DragDrop("div1", "group1");
16528  * </pre>
16529  * Since none of the event handlers have been implemented, nothing would
16530  * actually happen if you were to run the code above.  Normally you would
16531  * override this class or one of the default implementations, but you can
16532  * also override the methods you want on an instance of the class...
16533  * <pre>
16534  *  dd.onDragDrop = function(e, id) {
16535  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16536  *  }
16537  * </pre>
16538  * @constructor
16539  * @param {String} id of the element that is linked to this instance
16540  * @param {String} sGroup the group of related DragDrop objects
16541  * @param {object} config an object containing configurable attributes
16542  *                Valid properties for DragDrop:
16543  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16544  */
16545 Roo.dd.DragDrop = function(id, sGroup, config) {
16546     if (id) {
16547         this.init(id, sGroup, config);
16548     }
16549     
16550 };
16551
16552 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16553
16554     /**
16555      * The id of the element associated with this object.  This is what we
16556      * refer to as the "linked element" because the size and position of
16557      * this element is used to determine when the drag and drop objects have
16558      * interacted.
16559      * @property id
16560      * @type String
16561      */
16562     id: null,
16563
16564     /**
16565      * Configuration attributes passed into the constructor
16566      * @property config
16567      * @type object
16568      */
16569     config: null,
16570
16571     /**
16572      * The id of the element that will be dragged.  By default this is same
16573      * as the linked element , but could be changed to another element. Ex:
16574      * Roo.dd.DDProxy
16575      * @property dragElId
16576      * @type String
16577      * @private
16578      */
16579     dragElId: null,
16580
16581     /**
16582      * the id of the element that initiates the drag operation.  By default
16583      * this is the linked element, but could be changed to be a child of this
16584      * element.  This lets us do things like only starting the drag when the
16585      * header element within the linked html element is clicked.
16586      * @property handleElId
16587      * @type String
16588      * @private
16589      */
16590     handleElId: null,
16591
16592     /**
16593      * An associative array of HTML tags that will be ignored if clicked.
16594      * @property invalidHandleTypes
16595      * @type {string: string}
16596      */
16597     invalidHandleTypes: null,
16598
16599     /**
16600      * An associative array of ids for elements that will be ignored if clicked
16601      * @property invalidHandleIds
16602      * @type {string: string}
16603      */
16604     invalidHandleIds: null,
16605
16606     /**
16607      * An indexted array of css class names for elements that will be ignored
16608      * if clicked.
16609      * @property invalidHandleClasses
16610      * @type string[]
16611      */
16612     invalidHandleClasses: null,
16613
16614     /**
16615      * The linked element's absolute X position at the time the drag was
16616      * started
16617      * @property startPageX
16618      * @type int
16619      * @private
16620      */
16621     startPageX: 0,
16622
16623     /**
16624      * The linked element's absolute X position at the time the drag was
16625      * started
16626      * @property startPageY
16627      * @type int
16628      * @private
16629      */
16630     startPageY: 0,
16631
16632     /**
16633      * The group defines a logical collection of DragDrop objects that are
16634      * related.  Instances only get events when interacting with other
16635      * DragDrop object in the same group.  This lets us define multiple
16636      * groups using a single DragDrop subclass if we want.
16637      * @property groups
16638      * @type {string: string}
16639      */
16640     groups: null,
16641
16642     /**
16643      * Individual drag/drop instances can be locked.  This will prevent
16644      * onmousedown start drag.
16645      * @property locked
16646      * @type boolean
16647      * @private
16648      */
16649     locked: false,
16650
16651     /**
16652      * Lock this instance
16653      * @method lock
16654      */
16655     lock: function() { this.locked = true; },
16656
16657     /**
16658      * Unlock this instace
16659      * @method unlock
16660      */
16661     unlock: function() { this.locked = false; },
16662
16663     /**
16664      * By default, all insances can be a drop target.  This can be disabled by
16665      * setting isTarget to false.
16666      * @method isTarget
16667      * @type boolean
16668      */
16669     isTarget: true,
16670
16671     /**
16672      * The padding configured for this drag and drop object for calculating
16673      * the drop zone intersection with this object.
16674      * @method padding
16675      * @type int[]
16676      */
16677     padding: null,
16678
16679     /**
16680      * Cached reference to the linked element
16681      * @property _domRef
16682      * @private
16683      */
16684     _domRef: null,
16685
16686     /**
16687      * Internal typeof flag
16688      * @property __ygDragDrop
16689      * @private
16690      */
16691     __ygDragDrop: true,
16692
16693     /**
16694      * Set to true when horizontal contraints are applied
16695      * @property constrainX
16696      * @type boolean
16697      * @private
16698      */
16699     constrainX: false,
16700
16701     /**
16702      * Set to true when vertical contraints are applied
16703      * @property constrainY
16704      * @type boolean
16705      * @private
16706      */
16707     constrainY: false,
16708
16709     /**
16710      * The left constraint
16711      * @property minX
16712      * @type int
16713      * @private
16714      */
16715     minX: 0,
16716
16717     /**
16718      * The right constraint
16719      * @property maxX
16720      * @type int
16721      * @private
16722      */
16723     maxX: 0,
16724
16725     /**
16726      * The up constraint
16727      * @property minY
16728      * @type int
16729      * @type int
16730      * @private
16731      */
16732     minY: 0,
16733
16734     /**
16735      * The down constraint
16736      * @property maxY
16737      * @type int
16738      * @private
16739      */
16740     maxY: 0,
16741
16742     /**
16743      * Maintain offsets when we resetconstraints.  Set to true when you want
16744      * the position of the element relative to its parent to stay the same
16745      * when the page changes
16746      *
16747      * @property maintainOffset
16748      * @type boolean
16749      */
16750     maintainOffset: false,
16751
16752     /**
16753      * Array of pixel locations the element will snap to if we specified a
16754      * horizontal graduation/interval.  This array is generated automatically
16755      * when you define a tick interval.
16756      * @property xTicks
16757      * @type int[]
16758      */
16759     xTicks: null,
16760
16761     /**
16762      * Array of pixel locations the element will snap to if we specified a
16763      * vertical graduation/interval.  This array is generated automatically
16764      * when you define a tick interval.
16765      * @property yTicks
16766      * @type int[]
16767      */
16768     yTicks: null,
16769
16770     /**
16771      * By default the drag and drop instance will only respond to the primary
16772      * button click (left button for a right-handed mouse).  Set to true to
16773      * allow drag and drop to start with any mouse click that is propogated
16774      * by the browser
16775      * @property primaryButtonOnly
16776      * @type boolean
16777      */
16778     primaryButtonOnly: true,
16779
16780     /**
16781      * The availabe property is false until the linked dom element is accessible.
16782      * @property available
16783      * @type boolean
16784      */
16785     available: false,
16786
16787     /**
16788      * By default, drags can only be initiated if the mousedown occurs in the
16789      * region the linked element is.  This is done in part to work around a
16790      * bug in some browsers that mis-report the mousedown if the previous
16791      * mouseup happened outside of the window.  This property is set to true
16792      * if outer handles are defined.
16793      *
16794      * @property hasOuterHandles
16795      * @type boolean
16796      * @default false
16797      */
16798     hasOuterHandles: false,
16799
16800     /**
16801      * Code that executes immediately before the startDrag event
16802      * @method b4StartDrag
16803      * @private
16804      */
16805     b4StartDrag: function(x, y) { },
16806
16807     /**
16808      * Abstract method called after a drag/drop object is clicked
16809      * and the drag or mousedown time thresholds have beeen met.
16810      * @method startDrag
16811      * @param {int} X click location
16812      * @param {int} Y click location
16813      */
16814     startDrag: function(x, y) { /* override this */ },
16815
16816     /**
16817      * Code that executes immediately before the onDrag event
16818      * @method b4Drag
16819      * @private
16820      */
16821     b4Drag: function(e) { },
16822
16823     /**
16824      * Abstract method called during the onMouseMove event while dragging an
16825      * object.
16826      * @method onDrag
16827      * @param {Event} e the mousemove event
16828      */
16829     onDrag: function(e) { /* override this */ },
16830
16831     /**
16832      * Abstract method called when this element fist begins hovering over
16833      * another DragDrop obj
16834      * @method onDragEnter
16835      * @param {Event} e the mousemove event
16836      * @param {String|DragDrop[]} id In POINT mode, the element
16837      * id this is hovering over.  In INTERSECT mode, an array of one or more
16838      * dragdrop items being hovered over.
16839      */
16840     onDragEnter: function(e, id) { /* override this */ },
16841
16842     /**
16843      * Code that executes immediately before the onDragOver event
16844      * @method b4DragOver
16845      * @private
16846      */
16847     b4DragOver: function(e) { },
16848
16849     /**
16850      * Abstract method called when this element is hovering over another
16851      * DragDrop obj
16852      * @method onDragOver
16853      * @param {Event} e the mousemove event
16854      * @param {String|DragDrop[]} id In POINT mode, the element
16855      * id this is hovering over.  In INTERSECT mode, an array of dd items
16856      * being hovered over.
16857      */
16858     onDragOver: function(e, id) { /* override this */ },
16859
16860     /**
16861      * Code that executes immediately before the onDragOut event
16862      * @method b4DragOut
16863      * @private
16864      */
16865     b4DragOut: function(e) { },
16866
16867     /**
16868      * Abstract method called when we are no longer hovering over an element
16869      * @method onDragOut
16870      * @param {Event} e the mousemove event
16871      * @param {String|DragDrop[]} id In POINT mode, the element
16872      * id this was hovering over.  In INTERSECT mode, an array of dd items
16873      * that the mouse is no longer over.
16874      */
16875     onDragOut: function(e, id) { /* override this */ },
16876
16877     /**
16878      * Code that executes immediately before the onDragDrop event
16879      * @method b4DragDrop
16880      * @private
16881      */
16882     b4DragDrop: function(e) { },
16883
16884     /**
16885      * Abstract method called when this item is dropped on another DragDrop
16886      * obj
16887      * @method onDragDrop
16888      * @param {Event} e the mouseup event
16889      * @param {String|DragDrop[]} id In POINT mode, the element
16890      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16891      * was dropped on.
16892      */
16893     onDragDrop: function(e, id) { /* override this */ },
16894
16895     /**
16896      * Abstract method called when this item is dropped on an area with no
16897      * drop target
16898      * @method onInvalidDrop
16899      * @param {Event} e the mouseup event
16900      */
16901     onInvalidDrop: function(e) { /* override this */ },
16902
16903     /**
16904      * Code that executes immediately before the endDrag event
16905      * @method b4EndDrag
16906      * @private
16907      */
16908     b4EndDrag: function(e) { },
16909
16910     /**
16911      * Fired when we are done dragging the object
16912      * @method endDrag
16913      * @param {Event} e the mouseup event
16914      */
16915     endDrag: function(e) { /* override this */ },
16916
16917     /**
16918      * Code executed immediately before the onMouseDown event
16919      * @method b4MouseDown
16920      * @param {Event} e the mousedown event
16921      * @private
16922      */
16923     b4MouseDown: function(e) {  },
16924
16925     /**
16926      * Event handler that fires when a drag/drop obj gets a mousedown
16927      * @method onMouseDown
16928      * @param {Event} e the mousedown event
16929      */
16930     onMouseDown: function(e) { /* override this */ },
16931
16932     /**
16933      * Event handler that fires when a drag/drop obj gets a mouseup
16934      * @method onMouseUp
16935      * @param {Event} e the mouseup event
16936      */
16937     onMouseUp: function(e) { /* override this */ },
16938
16939     /**
16940      * Override the onAvailable method to do what is needed after the initial
16941      * position was determined.
16942      * @method onAvailable
16943      */
16944     onAvailable: function () {
16945     },
16946
16947     /*
16948      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16949      * @type Object
16950      */
16951     defaultPadding : {left:0, right:0, top:0, bottom:0},
16952
16953     /*
16954      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16955  *
16956  * Usage:
16957  <pre><code>
16958  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16959                 { dragElId: "existingProxyDiv" });
16960  dd.startDrag = function(){
16961      this.constrainTo("parent-id");
16962  };
16963  </code></pre>
16964  * Or you can initalize it using the {@link Roo.Element} object:
16965  <pre><code>
16966  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16967      startDrag : function(){
16968          this.constrainTo("parent-id");
16969      }
16970  });
16971  </code></pre>
16972      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16973      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16974      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16975      * an object containing the sides to pad. For example: {right:10, bottom:10}
16976      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16977      */
16978     constrainTo : function(constrainTo, pad, inContent){
16979         if(typeof pad == "number"){
16980             pad = {left: pad, right:pad, top:pad, bottom:pad};
16981         }
16982         pad = pad || this.defaultPadding;
16983         var b = Roo.get(this.getEl()).getBox();
16984         var ce = Roo.get(constrainTo);
16985         var s = ce.getScroll();
16986         var c, cd = ce.dom;
16987         if(cd == document.body){
16988             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16989         }else{
16990             xy = ce.getXY();
16991             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16992         }
16993
16994
16995         var topSpace = b.y - c.y;
16996         var leftSpace = b.x - c.x;
16997
16998         this.resetConstraints();
16999         this.setXConstraint(leftSpace - (pad.left||0), // left
17000                 c.width - leftSpace - b.width - (pad.right||0) //right
17001         );
17002         this.setYConstraint(topSpace - (pad.top||0), //top
17003                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
17004         );
17005     },
17006
17007     /**
17008      * Returns a reference to the linked element
17009      * @method getEl
17010      * @return {HTMLElement} the html element
17011      */
17012     getEl: function() {
17013         if (!this._domRef) {
17014             this._domRef = Roo.getDom(this.id);
17015         }
17016
17017         return this._domRef;
17018     },
17019
17020     /**
17021      * Returns a reference to the actual element to drag.  By default this is
17022      * the same as the html element, but it can be assigned to another
17023      * element. An example of this can be found in Roo.dd.DDProxy
17024      * @method getDragEl
17025      * @return {HTMLElement} the html element
17026      */
17027     getDragEl: function() {
17028         return Roo.getDom(this.dragElId);
17029     },
17030
17031     /**
17032      * Sets up the DragDrop object.  Must be called in the constructor of any
17033      * Roo.dd.DragDrop subclass
17034      * @method init
17035      * @param id the id of the linked element
17036      * @param {String} sGroup the group of related items
17037      * @param {object} config configuration attributes
17038      */
17039     init: function(id, sGroup, config) {
17040         this.initTarget(id, sGroup, config);
17041         if (!Roo.isTouch) {
17042             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17043         }
17044         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17045         // Event.on(this.id, "selectstart", Event.preventDefault);
17046     },
17047
17048     /**
17049      * Initializes Targeting functionality only... the object does not
17050      * get a mousedown handler.
17051      * @method initTarget
17052      * @param id the id of the linked element
17053      * @param {String} sGroup the group of related items
17054      * @param {object} config configuration attributes
17055      */
17056     initTarget: function(id, sGroup, config) {
17057
17058         // configuration attributes
17059         this.config = config || {};
17060
17061         // create a local reference to the drag and drop manager
17062         this.DDM = Roo.dd.DDM;
17063         // initialize the groups array
17064         this.groups = {};
17065
17066         // assume that we have an element reference instead of an id if the
17067         // parameter is not a string
17068         if (typeof id !== "string") {
17069             id = Roo.id(id);
17070         }
17071
17072         // set the id
17073         this.id = id;
17074
17075         // add to an interaction group
17076         this.addToGroup((sGroup) ? sGroup : "default");
17077
17078         // We don't want to register this as the handle with the manager
17079         // so we just set the id rather than calling the setter.
17080         this.handleElId = id;
17081
17082         // the linked element is the element that gets dragged by default
17083         this.setDragElId(id);
17084
17085         // by default, clicked anchors will not start drag operations.
17086         this.invalidHandleTypes = { A: "A" };
17087         this.invalidHandleIds = {};
17088         this.invalidHandleClasses = [];
17089
17090         this.applyConfig();
17091
17092         this.handleOnAvailable();
17093     },
17094
17095     /**
17096      * Applies the configuration parameters that were passed into the constructor.
17097      * This is supposed to happen at each level through the inheritance chain.  So
17098      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17099      * DragDrop in order to get all of the parameters that are available in
17100      * each object.
17101      * @method applyConfig
17102      */
17103     applyConfig: function() {
17104
17105         // configurable properties:
17106         //    padding, isTarget, maintainOffset, primaryButtonOnly
17107         this.padding           = this.config.padding || [0, 0, 0, 0];
17108         this.isTarget          = (this.config.isTarget !== false);
17109         this.maintainOffset    = (this.config.maintainOffset);
17110         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17111
17112     },
17113
17114     /**
17115      * Executed when the linked element is available
17116      * @method handleOnAvailable
17117      * @private
17118      */
17119     handleOnAvailable: function() {
17120         this.available = true;
17121         this.resetConstraints();
17122         this.onAvailable();
17123     },
17124
17125      /**
17126      * Configures the padding for the target zone in px.  Effectively expands
17127      * (or reduces) the virtual object size for targeting calculations.
17128      * Supports css-style shorthand; if only one parameter is passed, all sides
17129      * will have that padding, and if only two are passed, the top and bottom
17130      * will have the first param, the left and right the second.
17131      * @method setPadding
17132      * @param {int} iTop    Top pad
17133      * @param {int} iRight  Right pad
17134      * @param {int} iBot    Bot pad
17135      * @param {int} iLeft   Left pad
17136      */
17137     setPadding: function(iTop, iRight, iBot, iLeft) {
17138         // this.padding = [iLeft, iRight, iTop, iBot];
17139         if (!iRight && 0 !== iRight) {
17140             this.padding = [iTop, iTop, iTop, iTop];
17141         } else if (!iBot && 0 !== iBot) {
17142             this.padding = [iTop, iRight, iTop, iRight];
17143         } else {
17144             this.padding = [iTop, iRight, iBot, iLeft];
17145         }
17146     },
17147
17148     /**
17149      * Stores the initial placement of the linked element.
17150      * @method setInitialPosition
17151      * @param {int} diffX   the X offset, default 0
17152      * @param {int} diffY   the Y offset, default 0
17153      */
17154     setInitPosition: function(diffX, diffY) {
17155         var el = this.getEl();
17156
17157         if (!this.DDM.verifyEl(el)) {
17158             return;
17159         }
17160
17161         var dx = diffX || 0;
17162         var dy = diffY || 0;
17163
17164         var p = Dom.getXY( el );
17165
17166         this.initPageX = p[0] - dx;
17167         this.initPageY = p[1] - dy;
17168
17169         this.lastPageX = p[0];
17170         this.lastPageY = p[1];
17171
17172
17173         this.setStartPosition(p);
17174     },
17175
17176     /**
17177      * Sets the start position of the element.  This is set when the obj
17178      * is initialized, the reset when a drag is started.
17179      * @method setStartPosition
17180      * @param pos current position (from previous lookup)
17181      * @private
17182      */
17183     setStartPosition: function(pos) {
17184         var p = pos || Dom.getXY( this.getEl() );
17185         this.deltaSetXY = null;
17186
17187         this.startPageX = p[0];
17188         this.startPageY = p[1];
17189     },
17190
17191     /**
17192      * Add this instance to a group of related drag/drop objects.  All
17193      * instances belong to at least one group, and can belong to as many
17194      * groups as needed.
17195      * @method addToGroup
17196      * @param sGroup {string} the name of the group
17197      */
17198     addToGroup: function(sGroup) {
17199         this.groups[sGroup] = true;
17200         this.DDM.regDragDrop(this, sGroup);
17201     },
17202
17203     /**
17204      * Remove's this instance from the supplied interaction group
17205      * @method removeFromGroup
17206      * @param {string}  sGroup  The group to drop
17207      */
17208     removeFromGroup: function(sGroup) {
17209         if (this.groups[sGroup]) {
17210             delete this.groups[sGroup];
17211         }
17212
17213         this.DDM.removeDDFromGroup(this, sGroup);
17214     },
17215
17216     /**
17217      * Allows you to specify that an element other than the linked element
17218      * will be moved with the cursor during a drag
17219      * @method setDragElId
17220      * @param id {string} the id of the element that will be used to initiate the drag
17221      */
17222     setDragElId: function(id) {
17223         this.dragElId = id;
17224     },
17225
17226     /**
17227      * Allows you to specify a child of the linked element that should be
17228      * used to initiate the drag operation.  An example of this would be if
17229      * you have a content div with text and links.  Clicking anywhere in the
17230      * content area would normally start the drag operation.  Use this method
17231      * to specify that an element inside of the content div is the element
17232      * that starts the drag operation.
17233      * @method setHandleElId
17234      * @param id {string} the id of the element that will be used to
17235      * initiate the drag.
17236      */
17237     setHandleElId: function(id) {
17238         if (typeof id !== "string") {
17239             id = Roo.id(id);
17240         }
17241         this.handleElId = id;
17242         this.DDM.regHandle(this.id, id);
17243     },
17244
17245     /**
17246      * Allows you to set an element outside of the linked element as a drag
17247      * handle
17248      * @method setOuterHandleElId
17249      * @param id the id of the element that will be used to initiate the drag
17250      */
17251     setOuterHandleElId: function(id) {
17252         if (typeof id !== "string") {
17253             id = Roo.id(id);
17254         }
17255         Event.on(id, "mousedown",
17256                 this.handleMouseDown, this);
17257         this.setHandleElId(id);
17258
17259         this.hasOuterHandles = true;
17260     },
17261
17262     /**
17263      * Remove all drag and drop hooks for this element
17264      * @method unreg
17265      */
17266     unreg: function() {
17267         Event.un(this.id, "mousedown",
17268                 this.handleMouseDown);
17269         Event.un(this.id, "touchstart",
17270                 this.handleMouseDown);
17271         this._domRef = null;
17272         this.DDM._remove(this);
17273     },
17274
17275     destroy : function(){
17276         this.unreg();
17277     },
17278
17279     /**
17280      * Returns true if this instance is locked, or the drag drop mgr is locked
17281      * (meaning that all drag/drop is disabled on the page.)
17282      * @method isLocked
17283      * @return {boolean} true if this obj or all drag/drop is locked, else
17284      * false
17285      */
17286     isLocked: function() {
17287         return (this.DDM.isLocked() || this.locked);
17288     },
17289
17290     /**
17291      * Fired when this object is clicked
17292      * @method handleMouseDown
17293      * @param {Event} e
17294      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17295      * @private
17296      */
17297     handleMouseDown: function(e, oDD){
17298      
17299         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17300             //Roo.log('not touch/ button !=0');
17301             return;
17302         }
17303         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17304             return; // double touch..
17305         }
17306         
17307
17308         if (this.isLocked()) {
17309             //Roo.log('locked');
17310             return;
17311         }
17312
17313         this.DDM.refreshCache(this.groups);
17314 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17315         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17316         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17317             //Roo.log('no outer handes or not over target');
17318                 // do nothing.
17319         } else {
17320 //            Roo.log('check validator');
17321             if (this.clickValidator(e)) {
17322 //                Roo.log('validate success');
17323                 // set the initial element position
17324                 this.setStartPosition();
17325
17326
17327                 this.b4MouseDown(e);
17328                 this.onMouseDown(e);
17329
17330                 this.DDM.handleMouseDown(e, this);
17331
17332                 this.DDM.stopEvent(e);
17333             } else {
17334
17335
17336             }
17337         }
17338     },
17339
17340     clickValidator: function(e) {
17341         var target = e.getTarget();
17342         return ( this.isValidHandleChild(target) &&
17343                     (this.id == this.handleElId ||
17344                         this.DDM.handleWasClicked(target, this.id)) );
17345     },
17346
17347     /**
17348      * Allows you to specify a tag name that should not start a drag operation
17349      * when clicked.  This is designed to facilitate embedding links within a
17350      * drag handle that do something other than start the drag.
17351      * @method addInvalidHandleType
17352      * @param {string} tagName the type of element to exclude
17353      */
17354     addInvalidHandleType: function(tagName) {
17355         var type = tagName.toUpperCase();
17356         this.invalidHandleTypes[type] = type;
17357     },
17358
17359     /**
17360      * Lets you to specify an element id for a child of a drag handle
17361      * that should not initiate a drag
17362      * @method addInvalidHandleId
17363      * @param {string} id the element id of the element you wish to ignore
17364      */
17365     addInvalidHandleId: function(id) {
17366         if (typeof id !== "string") {
17367             id = Roo.id(id);
17368         }
17369         this.invalidHandleIds[id] = id;
17370     },
17371
17372     /**
17373      * Lets you specify a css class of elements that will not initiate a drag
17374      * @method addInvalidHandleClass
17375      * @param {string} cssClass the class of the elements you wish to ignore
17376      */
17377     addInvalidHandleClass: function(cssClass) {
17378         this.invalidHandleClasses.push(cssClass);
17379     },
17380
17381     /**
17382      * Unsets an excluded tag name set by addInvalidHandleType
17383      * @method removeInvalidHandleType
17384      * @param {string} tagName the type of element to unexclude
17385      */
17386     removeInvalidHandleType: function(tagName) {
17387         var type = tagName.toUpperCase();
17388         // this.invalidHandleTypes[type] = null;
17389         delete this.invalidHandleTypes[type];
17390     },
17391
17392     /**
17393      * Unsets an invalid handle id
17394      * @method removeInvalidHandleId
17395      * @param {string} id the id of the element to re-enable
17396      */
17397     removeInvalidHandleId: function(id) {
17398         if (typeof id !== "string") {
17399             id = Roo.id(id);
17400         }
17401         delete this.invalidHandleIds[id];
17402     },
17403
17404     /**
17405      * Unsets an invalid css class
17406      * @method removeInvalidHandleClass
17407      * @param {string} cssClass the class of the element(s) you wish to
17408      * re-enable
17409      */
17410     removeInvalidHandleClass: function(cssClass) {
17411         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17412             if (this.invalidHandleClasses[i] == cssClass) {
17413                 delete this.invalidHandleClasses[i];
17414             }
17415         }
17416     },
17417
17418     /**
17419      * Checks the tag exclusion list to see if this click should be ignored
17420      * @method isValidHandleChild
17421      * @param {HTMLElement} node the HTMLElement to evaluate
17422      * @return {boolean} true if this is a valid tag type, false if not
17423      */
17424     isValidHandleChild: function(node) {
17425
17426         var valid = true;
17427         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17428         var nodeName;
17429         try {
17430             nodeName = node.nodeName.toUpperCase();
17431         } catch(e) {
17432             nodeName = node.nodeName;
17433         }
17434         valid = valid && !this.invalidHandleTypes[nodeName];
17435         valid = valid && !this.invalidHandleIds[node.id];
17436
17437         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17438             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17439         }
17440
17441
17442         return valid;
17443
17444     },
17445
17446     /**
17447      * Create the array of horizontal tick marks if an interval was specified
17448      * in setXConstraint().
17449      * @method setXTicks
17450      * @private
17451      */
17452     setXTicks: function(iStartX, iTickSize) {
17453         this.xTicks = [];
17454         this.xTickSize = iTickSize;
17455
17456         var tickMap = {};
17457
17458         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17459             if (!tickMap[i]) {
17460                 this.xTicks[this.xTicks.length] = i;
17461                 tickMap[i] = true;
17462             }
17463         }
17464
17465         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17466             if (!tickMap[i]) {
17467                 this.xTicks[this.xTicks.length] = i;
17468                 tickMap[i] = true;
17469             }
17470         }
17471
17472         this.xTicks.sort(this.DDM.numericSort) ;
17473     },
17474
17475     /**
17476      * Create the array of vertical tick marks if an interval was specified in
17477      * setYConstraint().
17478      * @method setYTicks
17479      * @private
17480      */
17481     setYTicks: function(iStartY, iTickSize) {
17482         this.yTicks = [];
17483         this.yTickSize = iTickSize;
17484
17485         var tickMap = {};
17486
17487         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17488             if (!tickMap[i]) {
17489                 this.yTicks[this.yTicks.length] = i;
17490                 tickMap[i] = true;
17491             }
17492         }
17493
17494         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17495             if (!tickMap[i]) {
17496                 this.yTicks[this.yTicks.length] = i;
17497                 tickMap[i] = true;
17498             }
17499         }
17500
17501         this.yTicks.sort(this.DDM.numericSort) ;
17502     },
17503
17504     /**
17505      * By default, the element can be dragged any place on the screen.  Use
17506      * this method to limit the horizontal travel of the element.  Pass in
17507      * 0,0 for the parameters if you want to lock the drag to the y axis.
17508      * @method setXConstraint
17509      * @param {int} iLeft the number of pixels the element can move to the left
17510      * @param {int} iRight the number of pixels the element can move to the
17511      * right
17512      * @param {int} iTickSize optional parameter for specifying that the
17513      * element
17514      * should move iTickSize pixels at a time.
17515      */
17516     setXConstraint: function(iLeft, iRight, iTickSize) {
17517         this.leftConstraint = iLeft;
17518         this.rightConstraint = iRight;
17519
17520         this.minX = this.initPageX - iLeft;
17521         this.maxX = this.initPageX + iRight;
17522         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17523
17524         this.constrainX = true;
17525     },
17526
17527     /**
17528      * Clears any constraints applied to this instance.  Also clears ticks
17529      * since they can't exist independent of a constraint at this time.
17530      * @method clearConstraints
17531      */
17532     clearConstraints: function() {
17533         this.constrainX = false;
17534         this.constrainY = false;
17535         this.clearTicks();
17536     },
17537
17538     /**
17539      * Clears any tick interval defined for this instance
17540      * @method clearTicks
17541      */
17542     clearTicks: function() {
17543         this.xTicks = null;
17544         this.yTicks = null;
17545         this.xTickSize = 0;
17546         this.yTickSize = 0;
17547     },
17548
17549     /**
17550      * By default, the element can be dragged any place on the screen.  Set
17551      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17552      * parameters if you want to lock the drag to the x axis.
17553      * @method setYConstraint
17554      * @param {int} iUp the number of pixels the element can move up
17555      * @param {int} iDown the number of pixels the element can move down
17556      * @param {int} iTickSize optional parameter for specifying that the
17557      * element should move iTickSize pixels at a time.
17558      */
17559     setYConstraint: function(iUp, iDown, iTickSize) {
17560         this.topConstraint = iUp;
17561         this.bottomConstraint = iDown;
17562
17563         this.minY = this.initPageY - iUp;
17564         this.maxY = this.initPageY + iDown;
17565         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17566
17567         this.constrainY = true;
17568
17569     },
17570
17571     /**
17572      * resetConstraints must be called if you manually reposition a dd element.
17573      * @method resetConstraints
17574      * @param {boolean} maintainOffset
17575      */
17576     resetConstraints: function() {
17577
17578
17579         // Maintain offsets if necessary
17580         if (this.initPageX || this.initPageX === 0) {
17581             // figure out how much this thing has moved
17582             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17583             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17584
17585             this.setInitPosition(dx, dy);
17586
17587         // This is the first time we have detected the element's position
17588         } else {
17589             this.setInitPosition();
17590         }
17591
17592         if (this.constrainX) {
17593             this.setXConstraint( this.leftConstraint,
17594                                  this.rightConstraint,
17595                                  this.xTickSize        );
17596         }
17597
17598         if (this.constrainY) {
17599             this.setYConstraint( this.topConstraint,
17600                                  this.bottomConstraint,
17601                                  this.yTickSize         );
17602         }
17603     },
17604
17605     /**
17606      * Normally the drag element is moved pixel by pixel, but we can specify
17607      * that it move a number of pixels at a time.  This method resolves the
17608      * location when we have it set up like this.
17609      * @method getTick
17610      * @param {int} val where we want to place the object
17611      * @param {int[]} tickArray sorted array of valid points
17612      * @return {int} the closest tick
17613      * @private
17614      */
17615     getTick: function(val, tickArray) {
17616
17617         if (!tickArray) {
17618             // If tick interval is not defined, it is effectively 1 pixel,
17619             // so we return the value passed to us.
17620             return val;
17621         } else if (tickArray[0] >= val) {
17622             // The value is lower than the first tick, so we return the first
17623             // tick.
17624             return tickArray[0];
17625         } else {
17626             for (var i=0, len=tickArray.length; i<len; ++i) {
17627                 var next = i + 1;
17628                 if (tickArray[next] && tickArray[next] >= val) {
17629                     var diff1 = val - tickArray[i];
17630                     var diff2 = tickArray[next] - val;
17631                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17632                 }
17633             }
17634
17635             // The value is larger than the last tick, so we return the last
17636             // tick.
17637             return tickArray[tickArray.length - 1];
17638         }
17639     },
17640
17641     /**
17642      * toString method
17643      * @method toString
17644      * @return {string} string representation of the dd obj
17645      */
17646     toString: function() {
17647         return ("DragDrop " + this.id);
17648     }
17649
17650 });
17651
17652 })();
17653 /*
17654  * Based on:
17655  * Ext JS Library 1.1.1
17656  * Copyright(c) 2006-2007, Ext JS, LLC.
17657  *
17658  * Originally Released Under LGPL - original licence link has changed is not relivant.
17659  *
17660  * Fork - LGPL
17661  * <script type="text/javascript">
17662  */
17663
17664
17665 /**
17666  * The drag and drop utility provides a framework for building drag and drop
17667  * applications.  In addition to enabling drag and drop for specific elements,
17668  * the drag and drop elements are tracked by the manager class, and the
17669  * interactions between the various elements are tracked during the drag and
17670  * the implementing code is notified about these important moments.
17671  */
17672
17673 // Only load the library once.  Rewriting the manager class would orphan
17674 // existing drag and drop instances.
17675 if (!Roo.dd.DragDropMgr) {
17676
17677 /**
17678  * @class Roo.dd.DragDropMgr
17679  * DragDropMgr is a singleton that tracks the element interaction for
17680  * all DragDrop items in the window.  Generally, you will not call
17681  * this class directly, but it does have helper methods that could
17682  * be useful in your DragDrop implementations.
17683  * @singleton
17684  */
17685 Roo.dd.DragDropMgr = function() {
17686
17687     var Event = Roo.EventManager;
17688
17689     return {
17690
17691         /**
17692          * Two dimensional Array of registered DragDrop objects.  The first
17693          * dimension is the DragDrop item group, the second the DragDrop
17694          * object.
17695          * @property ids
17696          * @type {string: string}
17697          * @private
17698          * @static
17699          */
17700         ids: {},
17701
17702         /**
17703          * Array of element ids defined as drag handles.  Used to determine
17704          * if the element that generated the mousedown event is actually the
17705          * handle and not the html element itself.
17706          * @property handleIds
17707          * @type {string: string}
17708          * @private
17709          * @static
17710          */
17711         handleIds: {},
17712
17713         /**
17714          * the DragDrop object that is currently being dragged
17715          * @property dragCurrent
17716          * @type DragDrop
17717          * @private
17718          * @static
17719          **/
17720         dragCurrent: null,
17721
17722         /**
17723          * the DragDrop object(s) that are being hovered over
17724          * @property dragOvers
17725          * @type Array
17726          * @private
17727          * @static
17728          */
17729         dragOvers: {},
17730
17731         /**
17732          * the X distance between the cursor and the object being dragged
17733          * @property deltaX
17734          * @type int
17735          * @private
17736          * @static
17737          */
17738         deltaX: 0,
17739
17740         /**
17741          * the Y distance between the cursor and the object being dragged
17742          * @property deltaY
17743          * @type int
17744          * @private
17745          * @static
17746          */
17747         deltaY: 0,
17748
17749         /**
17750          * Flag to determine if we should prevent the default behavior of the
17751          * events we define. By default this is true, but this can be set to
17752          * false if you need the default behavior (not recommended)
17753          * @property preventDefault
17754          * @type boolean
17755          * @static
17756          */
17757         preventDefault: true,
17758
17759         /**
17760          * Flag to determine if we should stop the propagation of the events
17761          * we generate. This is true by default but you may want to set it to
17762          * false if the html element contains other features that require the
17763          * mouse click.
17764          * @property stopPropagation
17765          * @type boolean
17766          * @static
17767          */
17768         stopPropagation: true,
17769
17770         /**
17771          * Internal flag that is set to true when drag and drop has been
17772          * intialized
17773          * @property initialized
17774          * @private
17775          * @static
17776          */
17777         initalized: false,
17778
17779         /**
17780          * All drag and drop can be disabled.
17781          * @property locked
17782          * @private
17783          * @static
17784          */
17785         locked: false,
17786
17787         /**
17788          * Called the first time an element is registered.
17789          * @method init
17790          * @private
17791          * @static
17792          */
17793         init: function() {
17794             this.initialized = true;
17795         },
17796
17797         /**
17798          * In point mode, drag and drop interaction is defined by the
17799          * location of the cursor during the drag/drop
17800          * @property POINT
17801          * @type int
17802          * @static
17803          */
17804         POINT: 0,
17805
17806         /**
17807          * In intersect mode, drag and drop interactio nis defined by the
17808          * overlap of two or more drag and drop objects.
17809          * @property INTERSECT
17810          * @type int
17811          * @static
17812          */
17813         INTERSECT: 1,
17814
17815         /**
17816          * The current drag and drop mode.  Default: POINT
17817          * @property mode
17818          * @type int
17819          * @static
17820          */
17821         mode: 0,
17822
17823         /**
17824          * Runs method on all drag and drop objects
17825          * @method _execOnAll
17826          * @private
17827          * @static
17828          */
17829         _execOnAll: function(sMethod, args) {
17830             for (var i in this.ids) {
17831                 for (var j in this.ids[i]) {
17832                     var oDD = this.ids[i][j];
17833                     if (! this.isTypeOfDD(oDD)) {
17834                         continue;
17835                     }
17836                     oDD[sMethod].apply(oDD, args);
17837                 }
17838             }
17839         },
17840
17841         /**
17842          * Drag and drop initialization.  Sets up the global event handlers
17843          * @method _onLoad
17844          * @private
17845          * @static
17846          */
17847         _onLoad: function() {
17848
17849             this.init();
17850
17851             if (!Roo.isTouch) {
17852                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17853                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17854             }
17855             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17856             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17857             
17858             Event.on(window,   "unload",    this._onUnload, this, true);
17859             Event.on(window,   "resize",    this._onResize, this, true);
17860             // Event.on(window,   "mouseout",    this._test);
17861
17862         },
17863
17864         /**
17865          * Reset constraints on all drag and drop objs
17866          * @method _onResize
17867          * @private
17868          * @static
17869          */
17870         _onResize: function(e) {
17871             this._execOnAll("resetConstraints", []);
17872         },
17873
17874         /**
17875          * Lock all drag and drop functionality
17876          * @method lock
17877          * @static
17878          */
17879         lock: function() { this.locked = true; },
17880
17881         /**
17882          * Unlock all drag and drop functionality
17883          * @method unlock
17884          * @static
17885          */
17886         unlock: function() { this.locked = false; },
17887
17888         /**
17889          * Is drag and drop locked?
17890          * @method isLocked
17891          * @return {boolean} True if drag and drop is locked, false otherwise.
17892          * @static
17893          */
17894         isLocked: function() { return this.locked; },
17895
17896         /**
17897          * Location cache that is set for all drag drop objects when a drag is
17898          * initiated, cleared when the drag is finished.
17899          * @property locationCache
17900          * @private
17901          * @static
17902          */
17903         locationCache: {},
17904
17905         /**
17906          * Set useCache to false if you want to force object the lookup of each
17907          * drag and drop linked element constantly during a drag.
17908          * @property useCache
17909          * @type boolean
17910          * @static
17911          */
17912         useCache: true,
17913
17914         /**
17915          * The number of pixels that the mouse needs to move after the
17916          * mousedown before the drag is initiated.  Default=3;
17917          * @property clickPixelThresh
17918          * @type int
17919          * @static
17920          */
17921         clickPixelThresh: 3,
17922
17923         /**
17924          * The number of milliseconds after the mousedown event to initiate the
17925          * drag if we don't get a mouseup event. Default=1000
17926          * @property clickTimeThresh
17927          * @type int
17928          * @static
17929          */
17930         clickTimeThresh: 350,
17931
17932         /**
17933          * Flag that indicates that either the drag pixel threshold or the
17934          * mousdown time threshold has been met
17935          * @property dragThreshMet
17936          * @type boolean
17937          * @private
17938          * @static
17939          */
17940         dragThreshMet: false,
17941
17942         /**
17943          * Timeout used for the click time threshold
17944          * @property clickTimeout
17945          * @type Object
17946          * @private
17947          * @static
17948          */
17949         clickTimeout: null,
17950
17951         /**
17952          * The X position of the mousedown event stored for later use when a
17953          * drag threshold is met.
17954          * @property startX
17955          * @type int
17956          * @private
17957          * @static
17958          */
17959         startX: 0,
17960
17961         /**
17962          * The Y position of the mousedown event stored for later use when a
17963          * drag threshold is met.
17964          * @property startY
17965          * @type int
17966          * @private
17967          * @static
17968          */
17969         startY: 0,
17970
17971         /**
17972          * Each DragDrop instance must be registered with the DragDropMgr.
17973          * This is executed in DragDrop.init()
17974          * @method regDragDrop
17975          * @param {DragDrop} oDD the DragDrop object to register
17976          * @param {String} sGroup the name of the group this element belongs to
17977          * @static
17978          */
17979         regDragDrop: function(oDD, sGroup) {
17980             if (!this.initialized) { this.init(); }
17981
17982             if (!this.ids[sGroup]) {
17983                 this.ids[sGroup] = {};
17984             }
17985             this.ids[sGroup][oDD.id] = oDD;
17986         },
17987
17988         /**
17989          * Removes the supplied dd instance from the supplied group. Executed
17990          * by DragDrop.removeFromGroup, so don't call this function directly.
17991          * @method removeDDFromGroup
17992          * @private
17993          * @static
17994          */
17995         removeDDFromGroup: function(oDD, sGroup) {
17996             if (!this.ids[sGroup]) {
17997                 this.ids[sGroup] = {};
17998             }
17999
18000             var obj = this.ids[sGroup];
18001             if (obj && obj[oDD.id]) {
18002                 delete obj[oDD.id];
18003             }
18004         },
18005
18006         /**
18007          * Unregisters a drag and drop item.  This is executed in
18008          * DragDrop.unreg, use that method instead of calling this directly.
18009          * @method _remove
18010          * @private
18011          * @static
18012          */
18013         _remove: function(oDD) {
18014             for (var g in oDD.groups) {
18015                 if (g && this.ids[g][oDD.id]) {
18016                     delete this.ids[g][oDD.id];
18017                 }
18018             }
18019             delete this.handleIds[oDD.id];
18020         },
18021
18022         /**
18023          * Each DragDrop handle element must be registered.  This is done
18024          * automatically when executing DragDrop.setHandleElId()
18025          * @method regHandle
18026          * @param {String} sDDId the DragDrop id this element is a handle for
18027          * @param {String} sHandleId the id of the element that is the drag
18028          * handle
18029          * @static
18030          */
18031         regHandle: function(sDDId, sHandleId) {
18032             if (!this.handleIds[sDDId]) {
18033                 this.handleIds[sDDId] = {};
18034             }
18035             this.handleIds[sDDId][sHandleId] = sHandleId;
18036         },
18037
18038         /**
18039          * Utility function to determine if a given element has been
18040          * registered as a drag drop item.
18041          * @method isDragDrop
18042          * @param {String} id the element id to check
18043          * @return {boolean} true if this element is a DragDrop item,
18044          * false otherwise
18045          * @static
18046          */
18047         isDragDrop: function(id) {
18048             return ( this.getDDById(id) ) ? true : false;
18049         },
18050
18051         /**
18052          * Returns the drag and drop instances that are in all groups the
18053          * passed in instance belongs to.
18054          * @method getRelated
18055          * @param {DragDrop} p_oDD the obj to get related data for
18056          * @param {boolean} bTargetsOnly if true, only return targetable objs
18057          * @return {DragDrop[]} the related instances
18058          * @static
18059          */
18060         getRelated: function(p_oDD, bTargetsOnly) {
18061             var oDDs = [];
18062             for (var i in p_oDD.groups) {
18063                 for (j in this.ids[i]) {
18064                     var dd = this.ids[i][j];
18065                     if (! this.isTypeOfDD(dd)) {
18066                         continue;
18067                     }
18068                     if (!bTargetsOnly || dd.isTarget) {
18069                         oDDs[oDDs.length] = dd;
18070                     }
18071                 }
18072             }
18073
18074             return oDDs;
18075         },
18076
18077         /**
18078          * Returns true if the specified dd target is a legal target for
18079          * the specifice drag obj
18080          * @method isLegalTarget
18081          * @param {DragDrop} the drag obj
18082          * @param {DragDrop} the target
18083          * @return {boolean} true if the target is a legal target for the
18084          * dd obj
18085          * @static
18086          */
18087         isLegalTarget: function (oDD, oTargetDD) {
18088             var targets = this.getRelated(oDD, true);
18089             for (var i=0, len=targets.length;i<len;++i) {
18090                 if (targets[i].id == oTargetDD.id) {
18091                     return true;
18092                 }
18093             }
18094
18095             return false;
18096         },
18097
18098         /**
18099          * My goal is to be able to transparently determine if an object is
18100          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18101          * returns "object", oDD.constructor.toString() always returns
18102          * "DragDrop" and not the name of the subclass.  So for now it just
18103          * evaluates a well-known variable in DragDrop.
18104          * @method isTypeOfDD
18105          * @param {Object} the object to evaluate
18106          * @return {boolean} true if typeof oDD = DragDrop
18107          * @static
18108          */
18109         isTypeOfDD: function (oDD) {
18110             return (oDD && oDD.__ygDragDrop);
18111         },
18112
18113         /**
18114          * Utility function to determine if a given element has been
18115          * registered as a drag drop handle for the given Drag Drop object.
18116          * @method isHandle
18117          * @param {String} id the element id to check
18118          * @return {boolean} true if this element is a DragDrop handle, false
18119          * otherwise
18120          * @static
18121          */
18122         isHandle: function(sDDId, sHandleId) {
18123             return ( this.handleIds[sDDId] &&
18124                             this.handleIds[sDDId][sHandleId] );
18125         },
18126
18127         /**
18128          * Returns the DragDrop instance for a given id
18129          * @method getDDById
18130          * @param {String} id the id of the DragDrop object
18131          * @return {DragDrop} the drag drop object, null if it is not found
18132          * @static
18133          */
18134         getDDById: function(id) {
18135             for (var i in this.ids) {
18136                 if (this.ids[i][id]) {
18137                     return this.ids[i][id];
18138                 }
18139             }
18140             return null;
18141         },
18142
18143         /**
18144          * Fired after a registered DragDrop object gets the mousedown event.
18145          * Sets up the events required to track the object being dragged
18146          * @method handleMouseDown
18147          * @param {Event} e the event
18148          * @param oDD the DragDrop object being dragged
18149          * @private
18150          * @static
18151          */
18152         handleMouseDown: function(e, oDD) {
18153             if(Roo.QuickTips){
18154                 Roo.QuickTips.disable();
18155             }
18156             this.currentTarget = e.getTarget();
18157
18158             this.dragCurrent = oDD;
18159
18160             var el = oDD.getEl();
18161
18162             // track start position
18163             this.startX = e.getPageX();
18164             this.startY = e.getPageY();
18165
18166             this.deltaX = this.startX - el.offsetLeft;
18167             this.deltaY = this.startY - el.offsetTop;
18168
18169             this.dragThreshMet = false;
18170
18171             this.clickTimeout = setTimeout(
18172                     function() {
18173                         var DDM = Roo.dd.DDM;
18174                         DDM.startDrag(DDM.startX, DDM.startY);
18175                     },
18176                     this.clickTimeThresh );
18177         },
18178
18179         /**
18180          * Fired when either the drag pixel threshol or the mousedown hold
18181          * time threshold has been met.
18182          * @method startDrag
18183          * @param x {int} the X position of the original mousedown
18184          * @param y {int} the Y position of the original mousedown
18185          * @static
18186          */
18187         startDrag: function(x, y) {
18188             clearTimeout(this.clickTimeout);
18189             if (this.dragCurrent) {
18190                 this.dragCurrent.b4StartDrag(x, y);
18191                 this.dragCurrent.startDrag(x, y);
18192             }
18193             this.dragThreshMet = true;
18194         },
18195
18196         /**
18197          * Internal function to handle the mouseup event.  Will be invoked
18198          * from the context of the document.
18199          * @method handleMouseUp
18200          * @param {Event} e the event
18201          * @private
18202          * @static
18203          */
18204         handleMouseUp: function(e) {
18205
18206             if(Roo.QuickTips){
18207                 Roo.QuickTips.enable();
18208             }
18209             if (! this.dragCurrent) {
18210                 return;
18211             }
18212
18213             clearTimeout(this.clickTimeout);
18214
18215             if (this.dragThreshMet) {
18216                 this.fireEvents(e, true);
18217             } else {
18218             }
18219
18220             this.stopDrag(e);
18221
18222             this.stopEvent(e);
18223         },
18224
18225         /**
18226          * Utility to stop event propagation and event default, if these
18227          * features are turned on.
18228          * @method stopEvent
18229          * @param {Event} e the event as returned by this.getEvent()
18230          * @static
18231          */
18232         stopEvent: function(e){
18233             if(this.stopPropagation) {
18234                 e.stopPropagation();
18235             }
18236
18237             if (this.preventDefault) {
18238                 e.preventDefault();
18239             }
18240         },
18241
18242         /**
18243          * Internal function to clean up event handlers after the drag
18244          * operation is complete
18245          * @method stopDrag
18246          * @param {Event} e the event
18247          * @private
18248          * @static
18249          */
18250         stopDrag: function(e) {
18251             // Fire the drag end event for the item that was dragged
18252             if (this.dragCurrent) {
18253                 if (this.dragThreshMet) {
18254                     this.dragCurrent.b4EndDrag(e);
18255                     this.dragCurrent.endDrag(e);
18256                 }
18257
18258                 this.dragCurrent.onMouseUp(e);
18259             }
18260
18261             this.dragCurrent = null;
18262             this.dragOvers = {};
18263         },
18264
18265         /**
18266          * Internal function to handle the mousemove event.  Will be invoked
18267          * from the context of the html element.
18268          *
18269          * @TODO figure out what we can do about mouse events lost when the
18270          * user drags objects beyond the window boundary.  Currently we can
18271          * detect this in internet explorer by verifying that the mouse is
18272          * down during the mousemove event.  Firefox doesn't give us the
18273          * button state on the mousemove event.
18274          * @method handleMouseMove
18275          * @param {Event} e the event
18276          * @private
18277          * @static
18278          */
18279         handleMouseMove: function(e) {
18280             if (! this.dragCurrent) {
18281                 return true;
18282             }
18283
18284             // var button = e.which || e.button;
18285
18286             // check for IE mouseup outside of page boundary
18287             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18288                 this.stopEvent(e);
18289                 return this.handleMouseUp(e);
18290             }
18291
18292             if (!this.dragThreshMet) {
18293                 var diffX = Math.abs(this.startX - e.getPageX());
18294                 var diffY = Math.abs(this.startY - e.getPageY());
18295                 if (diffX > this.clickPixelThresh ||
18296                             diffY > this.clickPixelThresh) {
18297                     this.startDrag(this.startX, this.startY);
18298                 }
18299             }
18300
18301             if (this.dragThreshMet) {
18302                 this.dragCurrent.b4Drag(e);
18303                 this.dragCurrent.onDrag(e);
18304                 if(!this.dragCurrent.moveOnly){
18305                     this.fireEvents(e, false);
18306                 }
18307             }
18308
18309             this.stopEvent(e);
18310
18311             return true;
18312         },
18313
18314         /**
18315          * Iterates over all of the DragDrop elements to find ones we are
18316          * hovering over or dropping on
18317          * @method fireEvents
18318          * @param {Event} e the event
18319          * @param {boolean} isDrop is this a drop op or a mouseover op?
18320          * @private
18321          * @static
18322          */
18323         fireEvents: function(e, isDrop) {
18324             var dc = this.dragCurrent;
18325
18326             // If the user did the mouse up outside of the window, we could
18327             // get here even though we have ended the drag.
18328             if (!dc || dc.isLocked()) {
18329                 return;
18330             }
18331
18332             var pt = e.getPoint();
18333
18334             // cache the previous dragOver array
18335             var oldOvers = [];
18336
18337             var outEvts   = [];
18338             var overEvts  = [];
18339             var dropEvts  = [];
18340             var enterEvts = [];
18341
18342             // Check to see if the object(s) we were hovering over is no longer
18343             // being hovered over so we can fire the onDragOut event
18344             for (var i in this.dragOvers) {
18345
18346                 var ddo = this.dragOvers[i];
18347
18348                 if (! this.isTypeOfDD(ddo)) {
18349                     continue;
18350                 }
18351
18352                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18353                     outEvts.push( ddo );
18354                 }
18355
18356                 oldOvers[i] = true;
18357                 delete this.dragOvers[i];
18358             }
18359
18360             for (var sGroup in dc.groups) {
18361
18362                 if ("string" != typeof sGroup) {
18363                     continue;
18364                 }
18365
18366                 for (i in this.ids[sGroup]) {
18367                     var oDD = this.ids[sGroup][i];
18368                     if (! this.isTypeOfDD(oDD)) {
18369                         continue;
18370                     }
18371
18372                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18373                         if (this.isOverTarget(pt, oDD, this.mode)) {
18374                             // look for drop interactions
18375                             if (isDrop) {
18376                                 dropEvts.push( oDD );
18377                             // look for drag enter and drag over interactions
18378                             } else {
18379
18380                                 // initial drag over: dragEnter fires
18381                                 if (!oldOvers[oDD.id]) {
18382                                     enterEvts.push( oDD );
18383                                 // subsequent drag overs: dragOver fires
18384                                 } else {
18385                                     overEvts.push( oDD );
18386                                 }
18387
18388                                 this.dragOvers[oDD.id] = oDD;
18389                             }
18390                         }
18391                     }
18392                 }
18393             }
18394
18395             if (this.mode) {
18396                 if (outEvts.length) {
18397                     dc.b4DragOut(e, outEvts);
18398                     dc.onDragOut(e, outEvts);
18399                 }
18400
18401                 if (enterEvts.length) {
18402                     dc.onDragEnter(e, enterEvts);
18403                 }
18404
18405                 if (overEvts.length) {
18406                     dc.b4DragOver(e, overEvts);
18407                     dc.onDragOver(e, overEvts);
18408                 }
18409
18410                 if (dropEvts.length) {
18411                     dc.b4DragDrop(e, dropEvts);
18412                     dc.onDragDrop(e, dropEvts);
18413                 }
18414
18415             } else {
18416                 // fire dragout events
18417                 var len = 0;
18418                 for (i=0, len=outEvts.length; i<len; ++i) {
18419                     dc.b4DragOut(e, outEvts[i].id);
18420                     dc.onDragOut(e, outEvts[i].id);
18421                 }
18422
18423                 // fire enter events
18424                 for (i=0,len=enterEvts.length; i<len; ++i) {
18425                     // dc.b4DragEnter(e, oDD.id);
18426                     dc.onDragEnter(e, enterEvts[i].id);
18427                 }
18428
18429                 // fire over events
18430                 for (i=0,len=overEvts.length; i<len; ++i) {
18431                     dc.b4DragOver(e, overEvts[i].id);
18432                     dc.onDragOver(e, overEvts[i].id);
18433                 }
18434
18435                 // fire drop events
18436                 for (i=0, len=dropEvts.length; i<len; ++i) {
18437                     dc.b4DragDrop(e, dropEvts[i].id);
18438                     dc.onDragDrop(e, dropEvts[i].id);
18439                 }
18440
18441             }
18442
18443             // notify about a drop that did not find a target
18444             if (isDrop && !dropEvts.length) {
18445                 dc.onInvalidDrop(e);
18446             }
18447
18448         },
18449
18450         /**
18451          * Helper function for getting the best match from the list of drag
18452          * and drop objects returned by the drag and drop events when we are
18453          * in INTERSECT mode.  It returns either the first object that the
18454          * cursor is over, or the object that has the greatest overlap with
18455          * the dragged element.
18456          * @method getBestMatch
18457          * @param  {DragDrop[]} dds The array of drag and drop objects
18458          * targeted
18459          * @return {DragDrop}       The best single match
18460          * @static
18461          */
18462         getBestMatch: function(dds) {
18463             var winner = null;
18464             // Return null if the input is not what we expect
18465             //if (!dds || !dds.length || dds.length == 0) {
18466                // winner = null;
18467             // If there is only one item, it wins
18468             //} else if (dds.length == 1) {
18469
18470             var len = dds.length;
18471
18472             if (len == 1) {
18473                 winner = dds[0];
18474             } else {
18475                 // Loop through the targeted items
18476                 for (var i=0; i<len; ++i) {
18477                     var dd = dds[i];
18478                     // If the cursor is over the object, it wins.  If the
18479                     // cursor is over multiple matches, the first one we come
18480                     // to wins.
18481                     if (dd.cursorIsOver) {
18482                         winner = dd;
18483                         break;
18484                     // Otherwise the object with the most overlap wins
18485                     } else {
18486                         if (!winner ||
18487                             winner.overlap.getArea() < dd.overlap.getArea()) {
18488                             winner = dd;
18489                         }
18490                     }
18491                 }
18492             }
18493
18494             return winner;
18495         },
18496
18497         /**
18498          * Refreshes the cache of the top-left and bottom-right points of the
18499          * drag and drop objects in the specified group(s).  This is in the
18500          * format that is stored in the drag and drop instance, so typical
18501          * usage is:
18502          * <code>
18503          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18504          * </code>
18505          * Alternatively:
18506          * <code>
18507          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18508          * </code>
18509          * @TODO this really should be an indexed array.  Alternatively this
18510          * method could accept both.
18511          * @method refreshCache
18512          * @param {Object} groups an associative array of groups to refresh
18513          * @static
18514          */
18515         refreshCache: function(groups) {
18516             for (var sGroup in groups) {
18517                 if ("string" != typeof sGroup) {
18518                     continue;
18519                 }
18520                 for (var i in this.ids[sGroup]) {
18521                     var oDD = this.ids[sGroup][i];
18522
18523                     if (this.isTypeOfDD(oDD)) {
18524                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18525                         var loc = this.getLocation(oDD);
18526                         if (loc) {
18527                             this.locationCache[oDD.id] = loc;
18528                         } else {
18529                             delete this.locationCache[oDD.id];
18530                             // this will unregister the drag and drop object if
18531                             // the element is not in a usable state
18532                             // oDD.unreg();
18533                         }
18534                     }
18535                 }
18536             }
18537         },
18538
18539         /**
18540          * This checks to make sure an element exists and is in the DOM.  The
18541          * main purpose is to handle cases where innerHTML is used to remove
18542          * drag and drop objects from the DOM.  IE provides an 'unspecified
18543          * error' when trying to access the offsetParent of such an element
18544          * @method verifyEl
18545          * @param {HTMLElement} el the element to check
18546          * @return {boolean} true if the element looks usable
18547          * @static
18548          */
18549         verifyEl: function(el) {
18550             if (el) {
18551                 var parent;
18552                 if(Roo.isIE){
18553                     try{
18554                         parent = el.offsetParent;
18555                     }catch(e){}
18556                 }else{
18557                     parent = el.offsetParent;
18558                 }
18559                 if (parent) {
18560                     return true;
18561                 }
18562             }
18563
18564             return false;
18565         },
18566
18567         /**
18568          * Returns a Region object containing the drag and drop element's position
18569          * and size, including the padding configured for it
18570          * @method getLocation
18571          * @param {DragDrop} oDD the drag and drop object to get the
18572          *                       location for
18573          * @return {Roo.lib.Region} a Region object representing the total area
18574          *                             the element occupies, including any padding
18575          *                             the instance is configured for.
18576          * @static
18577          */
18578         getLocation: function(oDD) {
18579             if (! this.isTypeOfDD(oDD)) {
18580                 return null;
18581             }
18582
18583             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18584
18585             try {
18586                 pos= Roo.lib.Dom.getXY(el);
18587             } catch (e) { }
18588
18589             if (!pos) {
18590                 return null;
18591             }
18592
18593             x1 = pos[0];
18594             x2 = x1 + el.offsetWidth;
18595             y1 = pos[1];
18596             y2 = y1 + el.offsetHeight;
18597
18598             t = y1 - oDD.padding[0];
18599             r = x2 + oDD.padding[1];
18600             b = y2 + oDD.padding[2];
18601             l = x1 - oDD.padding[3];
18602
18603             return new Roo.lib.Region( t, r, b, l );
18604         },
18605
18606         /**
18607          * Checks the cursor location to see if it over the target
18608          * @method isOverTarget
18609          * @param {Roo.lib.Point} pt The point to evaluate
18610          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18611          * @return {boolean} true if the mouse is over the target
18612          * @private
18613          * @static
18614          */
18615         isOverTarget: function(pt, oTarget, intersect) {
18616             // use cache if available
18617             var loc = this.locationCache[oTarget.id];
18618             if (!loc || !this.useCache) {
18619                 loc = this.getLocation(oTarget);
18620                 this.locationCache[oTarget.id] = loc;
18621
18622             }
18623
18624             if (!loc) {
18625                 return false;
18626             }
18627
18628             oTarget.cursorIsOver = loc.contains( pt );
18629
18630             // DragDrop is using this as a sanity check for the initial mousedown
18631             // in this case we are done.  In POINT mode, if the drag obj has no
18632             // contraints, we are also done. Otherwise we need to evaluate the
18633             // location of the target as related to the actual location of the
18634             // dragged element.
18635             var dc = this.dragCurrent;
18636             if (!dc || !dc.getTargetCoord ||
18637                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18638                 return oTarget.cursorIsOver;
18639             }
18640
18641             oTarget.overlap = null;
18642
18643             // Get the current location of the drag element, this is the
18644             // location of the mouse event less the delta that represents
18645             // where the original mousedown happened on the element.  We
18646             // need to consider constraints and ticks as well.
18647             var pos = dc.getTargetCoord(pt.x, pt.y);
18648
18649             var el = dc.getDragEl();
18650             var curRegion = new Roo.lib.Region( pos.y,
18651                                                    pos.x + el.offsetWidth,
18652                                                    pos.y + el.offsetHeight,
18653                                                    pos.x );
18654
18655             var overlap = curRegion.intersect(loc);
18656
18657             if (overlap) {
18658                 oTarget.overlap = overlap;
18659                 return (intersect) ? true : oTarget.cursorIsOver;
18660             } else {
18661                 return false;
18662             }
18663         },
18664
18665         /**
18666          * unload event handler
18667          * @method _onUnload
18668          * @private
18669          * @static
18670          */
18671         _onUnload: function(e, me) {
18672             Roo.dd.DragDropMgr.unregAll();
18673         },
18674
18675         /**
18676          * Cleans up the drag and drop events and objects.
18677          * @method unregAll
18678          * @private
18679          * @static
18680          */
18681         unregAll: function() {
18682
18683             if (this.dragCurrent) {
18684                 this.stopDrag();
18685                 this.dragCurrent = null;
18686             }
18687
18688             this._execOnAll("unreg", []);
18689
18690             for (i in this.elementCache) {
18691                 delete this.elementCache[i];
18692             }
18693
18694             this.elementCache = {};
18695             this.ids = {};
18696         },
18697
18698         /**
18699          * A cache of DOM elements
18700          * @property elementCache
18701          * @private
18702          * @static
18703          */
18704         elementCache: {},
18705
18706         /**
18707          * Get the wrapper for the DOM element specified
18708          * @method getElWrapper
18709          * @param {String} id the id of the element to get
18710          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18711          * @private
18712          * @deprecated This wrapper isn't that useful
18713          * @static
18714          */
18715         getElWrapper: function(id) {
18716             var oWrapper = this.elementCache[id];
18717             if (!oWrapper || !oWrapper.el) {
18718                 oWrapper = this.elementCache[id] =
18719                     new this.ElementWrapper(Roo.getDom(id));
18720             }
18721             return oWrapper;
18722         },
18723
18724         /**
18725          * Returns the actual DOM element
18726          * @method getElement
18727          * @param {String} id the id of the elment to get
18728          * @return {Object} The element
18729          * @deprecated use Roo.getDom instead
18730          * @static
18731          */
18732         getElement: function(id) {
18733             return Roo.getDom(id);
18734         },
18735
18736         /**
18737          * Returns the style property for the DOM element (i.e.,
18738          * document.getElById(id).style)
18739          * @method getCss
18740          * @param {String} id the id of the elment to get
18741          * @return {Object} The style property of the element
18742          * @deprecated use Roo.getDom instead
18743          * @static
18744          */
18745         getCss: function(id) {
18746             var el = Roo.getDom(id);
18747             return (el) ? el.style : null;
18748         },
18749
18750         /**
18751          * Inner class for cached elements
18752          * @class DragDropMgr.ElementWrapper
18753          * @for DragDropMgr
18754          * @private
18755          * @deprecated
18756          */
18757         ElementWrapper: function(el) {
18758                 /**
18759                  * The element
18760                  * @property el
18761                  */
18762                 this.el = el || null;
18763                 /**
18764                  * The element id
18765                  * @property id
18766                  */
18767                 this.id = this.el && el.id;
18768                 /**
18769                  * A reference to the style property
18770                  * @property css
18771                  */
18772                 this.css = this.el && el.style;
18773             },
18774
18775         /**
18776          * Returns the X position of an html element
18777          * @method getPosX
18778          * @param el the element for which to get the position
18779          * @return {int} the X coordinate
18780          * @for DragDropMgr
18781          * @deprecated use Roo.lib.Dom.getX instead
18782          * @static
18783          */
18784         getPosX: function(el) {
18785             return Roo.lib.Dom.getX(el);
18786         },
18787
18788         /**
18789          * Returns the Y position of an html element
18790          * @method getPosY
18791          * @param el the element for which to get the position
18792          * @return {int} the Y coordinate
18793          * @deprecated use Roo.lib.Dom.getY instead
18794          * @static
18795          */
18796         getPosY: function(el) {
18797             return Roo.lib.Dom.getY(el);
18798         },
18799
18800         /**
18801          * Swap two nodes.  In IE, we use the native method, for others we
18802          * emulate the IE behavior
18803          * @method swapNode
18804          * @param n1 the first node to swap
18805          * @param n2 the other node to swap
18806          * @static
18807          */
18808         swapNode: function(n1, n2) {
18809             if (n1.swapNode) {
18810                 n1.swapNode(n2);
18811             } else {
18812                 var p = n2.parentNode;
18813                 var s = n2.nextSibling;
18814
18815                 if (s == n1) {
18816                     p.insertBefore(n1, n2);
18817                 } else if (n2 == n1.nextSibling) {
18818                     p.insertBefore(n2, n1);
18819                 } else {
18820                     n1.parentNode.replaceChild(n2, n1);
18821                     p.insertBefore(n1, s);
18822                 }
18823             }
18824         },
18825
18826         /**
18827          * Returns the current scroll position
18828          * @method getScroll
18829          * @private
18830          * @static
18831          */
18832         getScroll: function () {
18833             var t, l, dde=document.documentElement, db=document.body;
18834             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18835                 t = dde.scrollTop;
18836                 l = dde.scrollLeft;
18837             } else if (db) {
18838                 t = db.scrollTop;
18839                 l = db.scrollLeft;
18840             } else {
18841
18842             }
18843             return { top: t, left: l };
18844         },
18845
18846         /**
18847          * Returns the specified element style property
18848          * @method getStyle
18849          * @param {HTMLElement} el          the element
18850          * @param {string}      styleProp   the style property
18851          * @return {string} The value of the style property
18852          * @deprecated use Roo.lib.Dom.getStyle
18853          * @static
18854          */
18855         getStyle: function(el, styleProp) {
18856             return Roo.fly(el).getStyle(styleProp);
18857         },
18858
18859         /**
18860          * Gets the scrollTop
18861          * @method getScrollTop
18862          * @return {int} the document's scrollTop
18863          * @static
18864          */
18865         getScrollTop: function () { return this.getScroll().top; },
18866
18867         /**
18868          * Gets the scrollLeft
18869          * @method getScrollLeft
18870          * @return {int} the document's scrollTop
18871          * @static
18872          */
18873         getScrollLeft: function () { return this.getScroll().left; },
18874
18875         /**
18876          * Sets the x/y position of an element to the location of the
18877          * target element.
18878          * @method moveToEl
18879          * @param {HTMLElement} moveEl      The element to move
18880          * @param {HTMLElement} targetEl    The position reference element
18881          * @static
18882          */
18883         moveToEl: function (moveEl, targetEl) {
18884             var aCoord = Roo.lib.Dom.getXY(targetEl);
18885             Roo.lib.Dom.setXY(moveEl, aCoord);
18886         },
18887
18888         /**
18889          * Numeric array sort function
18890          * @method numericSort
18891          * @static
18892          */
18893         numericSort: function(a, b) { return (a - b); },
18894
18895         /**
18896          * Internal counter
18897          * @property _timeoutCount
18898          * @private
18899          * @static
18900          */
18901         _timeoutCount: 0,
18902
18903         /**
18904          * Trying to make the load order less important.  Without this we get
18905          * an error if this file is loaded before the Event Utility.
18906          * @method _addListeners
18907          * @private
18908          * @static
18909          */
18910         _addListeners: function() {
18911             var DDM = Roo.dd.DDM;
18912             if ( Roo.lib.Event && document ) {
18913                 DDM._onLoad();
18914             } else {
18915                 if (DDM._timeoutCount > 2000) {
18916                 } else {
18917                     setTimeout(DDM._addListeners, 10);
18918                     if (document && document.body) {
18919                         DDM._timeoutCount += 1;
18920                     }
18921                 }
18922             }
18923         },
18924
18925         /**
18926          * Recursively searches the immediate parent and all child nodes for
18927          * the handle element in order to determine wheter or not it was
18928          * clicked.
18929          * @method handleWasClicked
18930          * @param node the html element to inspect
18931          * @static
18932          */
18933         handleWasClicked: function(node, id) {
18934             if (this.isHandle(id, node.id)) {
18935                 return true;
18936             } else {
18937                 // check to see if this is a text node child of the one we want
18938                 var p = node.parentNode;
18939
18940                 while (p) {
18941                     if (this.isHandle(id, p.id)) {
18942                         return true;
18943                     } else {
18944                         p = p.parentNode;
18945                     }
18946                 }
18947             }
18948
18949             return false;
18950         }
18951
18952     };
18953
18954 }();
18955
18956 // shorter alias, save a few bytes
18957 Roo.dd.DDM = Roo.dd.DragDropMgr;
18958 Roo.dd.DDM._addListeners();
18959
18960 }/*
18961  * Based on:
18962  * Ext JS Library 1.1.1
18963  * Copyright(c) 2006-2007, Ext JS, LLC.
18964  *
18965  * Originally Released Under LGPL - original licence link has changed is not relivant.
18966  *
18967  * Fork - LGPL
18968  * <script type="text/javascript">
18969  */
18970
18971 /**
18972  * @class Roo.dd.DD
18973  * A DragDrop implementation where the linked element follows the
18974  * mouse cursor during a drag.
18975  * @extends Roo.dd.DragDrop
18976  * @constructor
18977  * @param {String} id the id of the linked element
18978  * @param {String} sGroup the group of related DragDrop items
18979  * @param {object} config an object containing configurable attributes
18980  *                Valid properties for DD:
18981  *                    scroll
18982  */
18983 Roo.dd.DD = function(id, sGroup, config) {
18984     if (id) {
18985         this.init(id, sGroup, config);
18986     }
18987 };
18988
18989 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18990
18991     /**
18992      * When set to true, the utility automatically tries to scroll the browser
18993      * window wehn a drag and drop element is dragged near the viewport boundary.
18994      * Defaults to true.
18995      * @property scroll
18996      * @type boolean
18997      */
18998     scroll: true,
18999
19000     /**
19001      * Sets the pointer offset to the distance between the linked element's top
19002      * left corner and the location the element was clicked
19003      * @method autoOffset
19004      * @param {int} iPageX the X coordinate of the click
19005      * @param {int} iPageY the Y coordinate of the click
19006      */
19007     autoOffset: function(iPageX, iPageY) {
19008         var x = iPageX - this.startPageX;
19009         var y = iPageY - this.startPageY;
19010         this.setDelta(x, y);
19011     },
19012
19013     /**
19014      * Sets the pointer offset.  You can call this directly to force the
19015      * offset to be in a particular location (e.g., pass in 0,0 to set it
19016      * to the center of the object)
19017      * @method setDelta
19018      * @param {int} iDeltaX the distance from the left
19019      * @param {int} iDeltaY the distance from the top
19020      */
19021     setDelta: function(iDeltaX, iDeltaY) {
19022         this.deltaX = iDeltaX;
19023         this.deltaY = iDeltaY;
19024     },
19025
19026     /**
19027      * Sets the drag element to the location of the mousedown or click event,
19028      * maintaining the cursor location relative to the location on the element
19029      * that was clicked.  Override this if you want to place the element in a
19030      * location other than where the cursor is.
19031      * @method setDragElPos
19032      * @param {int} iPageX the X coordinate of the mousedown or drag event
19033      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19034      */
19035     setDragElPos: function(iPageX, iPageY) {
19036         // the first time we do this, we are going to check to make sure
19037         // the element has css positioning
19038
19039         var el = this.getDragEl();
19040         this.alignElWithMouse(el, iPageX, iPageY);
19041     },
19042
19043     /**
19044      * Sets the element to the location of the mousedown or click event,
19045      * maintaining the cursor location relative to the location on the element
19046      * that was clicked.  Override this if you want to place the element in a
19047      * location other than where the cursor is.
19048      * @method alignElWithMouse
19049      * @param {HTMLElement} el the element to move
19050      * @param {int} iPageX the X coordinate of the mousedown or drag event
19051      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19052      */
19053     alignElWithMouse: function(el, iPageX, iPageY) {
19054         var oCoord = this.getTargetCoord(iPageX, iPageY);
19055         var fly = el.dom ? el : Roo.fly(el);
19056         if (!this.deltaSetXY) {
19057             var aCoord = [oCoord.x, oCoord.y];
19058             fly.setXY(aCoord);
19059             var newLeft = fly.getLeft(true);
19060             var newTop  = fly.getTop(true);
19061             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19062         } else {
19063             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19064         }
19065
19066         this.cachePosition(oCoord.x, oCoord.y);
19067         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19068         return oCoord;
19069     },
19070
19071     /**
19072      * Saves the most recent position so that we can reset the constraints and
19073      * tick marks on-demand.  We need to know this so that we can calculate the
19074      * number of pixels the element is offset from its original position.
19075      * @method cachePosition
19076      * @param iPageX the current x position (optional, this just makes it so we
19077      * don't have to look it up again)
19078      * @param iPageY the current y position (optional, this just makes it so we
19079      * don't have to look it up again)
19080      */
19081     cachePosition: function(iPageX, iPageY) {
19082         if (iPageX) {
19083             this.lastPageX = iPageX;
19084             this.lastPageY = iPageY;
19085         } else {
19086             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19087             this.lastPageX = aCoord[0];
19088             this.lastPageY = aCoord[1];
19089         }
19090     },
19091
19092     /**
19093      * Auto-scroll the window if the dragged object has been moved beyond the
19094      * visible window boundary.
19095      * @method autoScroll
19096      * @param {int} x the drag element's x position
19097      * @param {int} y the drag element's y position
19098      * @param {int} h the height of the drag element
19099      * @param {int} w the width of the drag element
19100      * @private
19101      */
19102     autoScroll: function(x, y, h, w) {
19103
19104         if (this.scroll) {
19105             // The client height
19106             var clientH = Roo.lib.Dom.getViewWidth();
19107
19108             // The client width
19109             var clientW = Roo.lib.Dom.getViewHeight();
19110
19111             // The amt scrolled down
19112             var st = this.DDM.getScrollTop();
19113
19114             // The amt scrolled right
19115             var sl = this.DDM.getScrollLeft();
19116
19117             // Location of the bottom of the element
19118             var bot = h + y;
19119
19120             // Location of the right of the element
19121             var right = w + x;
19122
19123             // The distance from the cursor to the bottom of the visible area,
19124             // adjusted so that we don't scroll if the cursor is beyond the
19125             // element drag constraints
19126             var toBot = (clientH + st - y - this.deltaY);
19127
19128             // The distance from the cursor to the right of the visible area
19129             var toRight = (clientW + sl - x - this.deltaX);
19130
19131
19132             // How close to the edge the cursor must be before we scroll
19133             // var thresh = (document.all) ? 100 : 40;
19134             var thresh = 40;
19135
19136             // How many pixels to scroll per autoscroll op.  This helps to reduce
19137             // clunky scrolling. IE is more sensitive about this ... it needs this
19138             // value to be higher.
19139             var scrAmt = (document.all) ? 80 : 30;
19140
19141             // Scroll down if we are near the bottom of the visible page and the
19142             // obj extends below the crease
19143             if ( bot > clientH && toBot < thresh ) {
19144                 window.scrollTo(sl, st + scrAmt);
19145             }
19146
19147             // Scroll up if the window is scrolled down and the top of the object
19148             // goes above the top border
19149             if ( y < st && st > 0 && y - st < thresh ) {
19150                 window.scrollTo(sl, st - scrAmt);
19151             }
19152
19153             // Scroll right if the obj is beyond the right border and the cursor is
19154             // near the border.
19155             if ( right > clientW && toRight < thresh ) {
19156                 window.scrollTo(sl + scrAmt, st);
19157             }
19158
19159             // Scroll left if the window has been scrolled to the right and the obj
19160             // extends past the left border
19161             if ( x < sl && sl > 0 && x - sl < thresh ) {
19162                 window.scrollTo(sl - scrAmt, st);
19163             }
19164         }
19165     },
19166
19167     /**
19168      * Finds the location the element should be placed if we want to move
19169      * it to where the mouse location less the click offset would place us.
19170      * @method getTargetCoord
19171      * @param {int} iPageX the X coordinate of the click
19172      * @param {int} iPageY the Y coordinate of the click
19173      * @return an object that contains the coordinates (Object.x and Object.y)
19174      * @private
19175      */
19176     getTargetCoord: function(iPageX, iPageY) {
19177
19178
19179         var x = iPageX - this.deltaX;
19180         var y = iPageY - this.deltaY;
19181
19182         if (this.constrainX) {
19183             if (x < this.minX) { x = this.minX; }
19184             if (x > this.maxX) { x = this.maxX; }
19185         }
19186
19187         if (this.constrainY) {
19188             if (y < this.minY) { y = this.minY; }
19189             if (y > this.maxY) { y = this.maxY; }
19190         }
19191
19192         x = this.getTick(x, this.xTicks);
19193         y = this.getTick(y, this.yTicks);
19194
19195
19196         return {x:x, y:y};
19197     },
19198
19199     /*
19200      * Sets up config options specific to this class. Overrides
19201      * Roo.dd.DragDrop, but all versions of this method through the
19202      * inheritance chain are called
19203      */
19204     applyConfig: function() {
19205         Roo.dd.DD.superclass.applyConfig.call(this);
19206         this.scroll = (this.config.scroll !== false);
19207     },
19208
19209     /*
19210      * Event that fires prior to the onMouseDown event.  Overrides
19211      * Roo.dd.DragDrop.
19212      */
19213     b4MouseDown: function(e) {
19214         // this.resetConstraints();
19215         this.autoOffset(e.getPageX(),
19216                             e.getPageY());
19217     },
19218
19219     /*
19220      * Event that fires prior to the onDrag event.  Overrides
19221      * Roo.dd.DragDrop.
19222      */
19223     b4Drag: function(e) {
19224         this.setDragElPos(e.getPageX(),
19225                             e.getPageY());
19226     },
19227
19228     toString: function() {
19229         return ("DD " + this.id);
19230     }
19231
19232     //////////////////////////////////////////////////////////////////////////
19233     // Debugging ygDragDrop events that can be overridden
19234     //////////////////////////////////////////////////////////////////////////
19235     /*
19236     startDrag: function(x, y) {
19237     },
19238
19239     onDrag: function(e) {
19240     },
19241
19242     onDragEnter: function(e, id) {
19243     },
19244
19245     onDragOver: function(e, id) {
19246     },
19247
19248     onDragOut: function(e, id) {
19249     },
19250
19251     onDragDrop: function(e, id) {
19252     },
19253
19254     endDrag: function(e) {
19255     }
19256
19257     */
19258
19259 });/*
19260  * Based on:
19261  * Ext JS Library 1.1.1
19262  * Copyright(c) 2006-2007, Ext JS, LLC.
19263  *
19264  * Originally Released Under LGPL - original licence link has changed is not relivant.
19265  *
19266  * Fork - LGPL
19267  * <script type="text/javascript">
19268  */
19269
19270 /**
19271  * @class Roo.dd.DDProxy
19272  * A DragDrop implementation that inserts an empty, bordered div into
19273  * the document that follows the cursor during drag operations.  At the time of
19274  * the click, the frame div is resized to the dimensions of the linked html
19275  * element, and moved to the exact location of the linked element.
19276  *
19277  * References to the "frame" element refer to the single proxy element that
19278  * was created to be dragged in place of all DDProxy elements on the
19279  * page.
19280  *
19281  * @extends Roo.dd.DD
19282  * @constructor
19283  * @param {String} id the id of the linked html element
19284  * @param {String} sGroup the group of related DragDrop objects
19285  * @param {object} config an object containing configurable attributes
19286  *                Valid properties for DDProxy in addition to those in DragDrop:
19287  *                   resizeFrame, centerFrame, dragElId
19288  */
19289 Roo.dd.DDProxy = function(id, sGroup, config) {
19290     if (id) {
19291         this.init(id, sGroup, config);
19292         this.initFrame();
19293     }
19294 };
19295
19296 /**
19297  * The default drag frame div id
19298  * @property Roo.dd.DDProxy.dragElId
19299  * @type String
19300  * @static
19301  */
19302 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19303
19304 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19305
19306     /**
19307      * By default we resize the drag frame to be the same size as the element
19308      * we want to drag (this is to get the frame effect).  We can turn it off
19309      * if we want a different behavior.
19310      * @property resizeFrame
19311      * @type boolean
19312      */
19313     resizeFrame: true,
19314
19315     /**
19316      * By default the frame is positioned exactly where the drag element is, so
19317      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19318      * you do not have constraints on the obj is to have the drag frame centered
19319      * around the cursor.  Set centerFrame to true for this effect.
19320      * @property centerFrame
19321      * @type boolean
19322      */
19323     centerFrame: false,
19324
19325     /**
19326      * Creates the proxy element if it does not yet exist
19327      * @method createFrame
19328      */
19329     createFrame: function() {
19330         var self = this;
19331         var body = document.body;
19332
19333         if (!body || !body.firstChild) {
19334             setTimeout( function() { self.createFrame(); }, 50 );
19335             return;
19336         }
19337
19338         var div = this.getDragEl();
19339
19340         if (!div) {
19341             div    = document.createElement("div");
19342             div.id = this.dragElId;
19343             var s  = div.style;
19344
19345             s.position   = "absolute";
19346             s.visibility = "hidden";
19347             s.cursor     = "move";
19348             s.border     = "2px solid #aaa";
19349             s.zIndex     = 999;
19350
19351             // appendChild can blow up IE if invoked prior to the window load event
19352             // while rendering a table.  It is possible there are other scenarios
19353             // that would cause this to happen as well.
19354             body.insertBefore(div, body.firstChild);
19355         }
19356     },
19357
19358     /**
19359      * Initialization for the drag frame element.  Must be called in the
19360      * constructor of all subclasses
19361      * @method initFrame
19362      */
19363     initFrame: function() {
19364         this.createFrame();
19365     },
19366
19367     applyConfig: function() {
19368         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19369
19370         this.resizeFrame = (this.config.resizeFrame !== false);
19371         this.centerFrame = (this.config.centerFrame);
19372         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19373     },
19374
19375     /**
19376      * Resizes the drag frame to the dimensions of the clicked object, positions
19377      * it over the object, and finally displays it
19378      * @method showFrame
19379      * @param {int} iPageX X click position
19380      * @param {int} iPageY Y click position
19381      * @private
19382      */
19383     showFrame: function(iPageX, iPageY) {
19384         var el = this.getEl();
19385         var dragEl = this.getDragEl();
19386         var s = dragEl.style;
19387
19388         this._resizeProxy();
19389
19390         if (this.centerFrame) {
19391             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19392                            Math.round(parseInt(s.height, 10)/2) );
19393         }
19394
19395         this.setDragElPos(iPageX, iPageY);
19396
19397         Roo.fly(dragEl).show();
19398     },
19399
19400     /**
19401      * The proxy is automatically resized to the dimensions of the linked
19402      * element when a drag is initiated, unless resizeFrame is set to false
19403      * @method _resizeProxy
19404      * @private
19405      */
19406     _resizeProxy: function() {
19407         if (this.resizeFrame) {
19408             var el = this.getEl();
19409             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19410         }
19411     },
19412
19413     // overrides Roo.dd.DragDrop
19414     b4MouseDown: function(e) {
19415         var x = e.getPageX();
19416         var y = e.getPageY();
19417         this.autoOffset(x, y);
19418         this.setDragElPos(x, y);
19419     },
19420
19421     // overrides Roo.dd.DragDrop
19422     b4StartDrag: function(x, y) {
19423         // show the drag frame
19424         this.showFrame(x, y);
19425     },
19426
19427     // overrides Roo.dd.DragDrop
19428     b4EndDrag: function(e) {
19429         Roo.fly(this.getDragEl()).hide();
19430     },
19431
19432     // overrides Roo.dd.DragDrop
19433     // By default we try to move the element to the last location of the frame.
19434     // This is so that the default behavior mirrors that of Roo.dd.DD.
19435     endDrag: function(e) {
19436
19437         var lel = this.getEl();
19438         var del = this.getDragEl();
19439
19440         // Show the drag frame briefly so we can get its position
19441         del.style.visibility = "";
19442
19443         this.beforeMove();
19444         // Hide the linked element before the move to get around a Safari
19445         // rendering bug.
19446         lel.style.visibility = "hidden";
19447         Roo.dd.DDM.moveToEl(lel, del);
19448         del.style.visibility = "hidden";
19449         lel.style.visibility = "";
19450
19451         this.afterDrag();
19452     },
19453
19454     beforeMove : function(){
19455
19456     },
19457
19458     afterDrag : function(){
19459
19460     },
19461
19462     toString: function() {
19463         return ("DDProxy " + this.id);
19464     }
19465
19466 });
19467 /*
19468  * Based on:
19469  * Ext JS Library 1.1.1
19470  * Copyright(c) 2006-2007, Ext JS, LLC.
19471  *
19472  * Originally Released Under LGPL - original licence link has changed is not relivant.
19473  *
19474  * Fork - LGPL
19475  * <script type="text/javascript">
19476  */
19477
19478  /**
19479  * @class Roo.dd.DDTarget
19480  * A DragDrop implementation that does not move, but can be a drop
19481  * target.  You would get the same result by simply omitting implementation
19482  * for the event callbacks, but this way we reduce the processing cost of the
19483  * event listener and the callbacks.
19484  * @extends Roo.dd.DragDrop
19485  * @constructor
19486  * @param {String} id the id of the element that is a drop target
19487  * @param {String} sGroup the group of related DragDrop objects
19488  * @param {object} config an object containing configurable attributes
19489  *                 Valid properties for DDTarget in addition to those in
19490  *                 DragDrop:
19491  *                    none
19492  */
19493 Roo.dd.DDTarget = function(id, sGroup, config) {
19494     if (id) {
19495         this.initTarget(id, sGroup, config);
19496     }
19497     if (config.listeners || config.events) { 
19498        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19499             listeners : config.listeners || {}, 
19500             events : config.events || {} 
19501         });    
19502     }
19503 };
19504
19505 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19506 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19507     toString: function() {
19508         return ("DDTarget " + this.id);
19509     }
19510 });
19511 /*
19512  * Based on:
19513  * Ext JS Library 1.1.1
19514  * Copyright(c) 2006-2007, Ext JS, LLC.
19515  *
19516  * Originally Released Under LGPL - original licence link has changed is not relivant.
19517  *
19518  * Fork - LGPL
19519  * <script type="text/javascript">
19520  */
19521  
19522
19523 /**
19524  * @class Roo.dd.ScrollManager
19525  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19526  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19527  * @singleton
19528  */
19529 Roo.dd.ScrollManager = function(){
19530     var ddm = Roo.dd.DragDropMgr;
19531     var els = {};
19532     var dragEl = null;
19533     var proc = {};
19534     
19535     
19536     
19537     var onStop = function(e){
19538         dragEl = null;
19539         clearProc();
19540     };
19541     
19542     var triggerRefresh = function(){
19543         if(ddm.dragCurrent){
19544              ddm.refreshCache(ddm.dragCurrent.groups);
19545         }
19546     };
19547     
19548     var doScroll = function(){
19549         if(ddm.dragCurrent){
19550             var dds = Roo.dd.ScrollManager;
19551             if(!dds.animate){
19552                 if(proc.el.scroll(proc.dir, dds.increment)){
19553                     triggerRefresh();
19554                 }
19555             }else{
19556                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19557             }
19558         }
19559     };
19560     
19561     var clearProc = function(){
19562         if(proc.id){
19563             clearInterval(proc.id);
19564         }
19565         proc.id = 0;
19566         proc.el = null;
19567         proc.dir = "";
19568     };
19569     
19570     var startProc = function(el, dir){
19571          Roo.log('scroll startproc');
19572         clearProc();
19573         proc.el = el;
19574         proc.dir = dir;
19575         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19576     };
19577     
19578     var onFire = function(e, isDrop){
19579        
19580         if(isDrop || !ddm.dragCurrent){ return; }
19581         var dds = Roo.dd.ScrollManager;
19582         if(!dragEl || dragEl != ddm.dragCurrent){
19583             dragEl = ddm.dragCurrent;
19584             // refresh regions on drag start
19585             dds.refreshCache();
19586         }
19587         
19588         var xy = Roo.lib.Event.getXY(e);
19589         var pt = new Roo.lib.Point(xy[0], xy[1]);
19590         for(var id in els){
19591             var el = els[id], r = el._region;
19592             if(r && r.contains(pt) && el.isScrollable()){
19593                 if(r.bottom - pt.y <= dds.thresh){
19594                     if(proc.el != el){
19595                         startProc(el, "down");
19596                     }
19597                     return;
19598                 }else if(r.right - pt.x <= dds.thresh){
19599                     if(proc.el != el){
19600                         startProc(el, "left");
19601                     }
19602                     return;
19603                 }else if(pt.y - r.top <= dds.thresh){
19604                     if(proc.el != el){
19605                         startProc(el, "up");
19606                     }
19607                     return;
19608                 }else if(pt.x - r.left <= dds.thresh){
19609                     if(proc.el != el){
19610                         startProc(el, "right");
19611                     }
19612                     return;
19613                 }
19614             }
19615         }
19616         clearProc();
19617     };
19618     
19619     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19620     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19621     
19622     return {
19623         /**
19624          * Registers new overflow element(s) to auto scroll
19625          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19626          */
19627         register : function(el){
19628             if(el instanceof Array){
19629                 for(var i = 0, len = el.length; i < len; i++) {
19630                         this.register(el[i]);
19631                 }
19632             }else{
19633                 el = Roo.get(el);
19634                 els[el.id] = el;
19635             }
19636             Roo.dd.ScrollManager.els = els;
19637         },
19638         
19639         /**
19640          * Unregisters overflow element(s) so they are no longer scrolled
19641          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19642          */
19643         unregister : function(el){
19644             if(el instanceof Array){
19645                 for(var i = 0, len = el.length; i < len; i++) {
19646                         this.unregister(el[i]);
19647                 }
19648             }else{
19649                 el = Roo.get(el);
19650                 delete els[el.id];
19651             }
19652         },
19653         
19654         /**
19655          * The number of pixels from the edge of a container the pointer needs to be to 
19656          * trigger scrolling (defaults to 25)
19657          * @type Number
19658          */
19659         thresh : 25,
19660         
19661         /**
19662          * The number of pixels to scroll in each scroll increment (defaults to 50)
19663          * @type Number
19664          */
19665         increment : 100,
19666         
19667         /**
19668          * The frequency of scrolls in milliseconds (defaults to 500)
19669          * @type Number
19670          */
19671         frequency : 500,
19672         
19673         /**
19674          * True to animate the scroll (defaults to true)
19675          * @type Boolean
19676          */
19677         animate: true,
19678         
19679         /**
19680          * The animation duration in seconds - 
19681          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19682          * @type Number
19683          */
19684         animDuration: .4,
19685         
19686         /**
19687          * Manually trigger a cache refresh.
19688          */
19689         refreshCache : function(){
19690             for(var id in els){
19691                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19692                     els[id]._region = els[id].getRegion();
19693                 }
19694             }
19695         }
19696     };
19697 }();/*
19698  * Based on:
19699  * Ext JS Library 1.1.1
19700  * Copyright(c) 2006-2007, Ext JS, LLC.
19701  *
19702  * Originally Released Under LGPL - original licence link has changed is not relivant.
19703  *
19704  * Fork - LGPL
19705  * <script type="text/javascript">
19706  */
19707  
19708
19709 /**
19710  * @class Roo.dd.Registry
19711  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19712  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19713  * @singleton
19714  */
19715 Roo.dd.Registry = function(){
19716     var elements = {}; 
19717     var handles = {}; 
19718     var autoIdSeed = 0;
19719
19720     var getId = function(el, autogen){
19721         if(typeof el == "string"){
19722             return el;
19723         }
19724         var id = el.id;
19725         if(!id && autogen !== false){
19726             id = "roodd-" + (++autoIdSeed);
19727             el.id = id;
19728         }
19729         return id;
19730     };
19731     
19732     return {
19733     /**
19734      * Register a drag drop element
19735      * @param {String|HTMLElement} element The id or DOM node to register
19736      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19737      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19738      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19739      * populated in the data object (if applicable):
19740      * <pre>
19741 Value      Description<br />
19742 ---------  ------------------------------------------<br />
19743 handles    Array of DOM nodes that trigger dragging<br />
19744            for the element being registered<br />
19745 isHandle   True if the element passed in triggers<br />
19746            dragging itself, else false
19747 </pre>
19748      */
19749         register : function(el, data){
19750             data = data || {};
19751             if(typeof el == "string"){
19752                 el = document.getElementById(el);
19753             }
19754             data.ddel = el;
19755             elements[getId(el)] = data;
19756             if(data.isHandle !== false){
19757                 handles[data.ddel.id] = data;
19758             }
19759             if(data.handles){
19760                 var hs = data.handles;
19761                 for(var i = 0, len = hs.length; i < len; i++){
19762                         handles[getId(hs[i])] = data;
19763                 }
19764             }
19765         },
19766
19767     /**
19768      * Unregister a drag drop element
19769      * @param {String|HTMLElement}  element The id or DOM node to unregister
19770      */
19771         unregister : function(el){
19772             var id = getId(el, false);
19773             var data = elements[id];
19774             if(data){
19775                 delete elements[id];
19776                 if(data.handles){
19777                     var hs = data.handles;
19778                     for(var i = 0, len = hs.length; i < len; i++){
19779                         delete handles[getId(hs[i], false)];
19780                     }
19781                 }
19782             }
19783         },
19784
19785     /**
19786      * Returns the handle registered for a DOM Node by id
19787      * @param {String|HTMLElement} id The DOM node or id to look up
19788      * @return {Object} handle The custom handle data
19789      */
19790         getHandle : function(id){
19791             if(typeof id != "string"){ // must be element?
19792                 id = id.id;
19793             }
19794             return handles[id];
19795         },
19796
19797     /**
19798      * Returns the handle that is registered for the DOM node that is the target of the event
19799      * @param {Event} e The event
19800      * @return {Object} handle The custom handle data
19801      */
19802         getHandleFromEvent : function(e){
19803             var t = Roo.lib.Event.getTarget(e);
19804             return t ? handles[t.id] : null;
19805         },
19806
19807     /**
19808      * Returns a custom data object that is registered for a DOM node by id
19809      * @param {String|HTMLElement} id The DOM node or id to look up
19810      * @return {Object} data The custom data
19811      */
19812         getTarget : function(id){
19813             if(typeof id != "string"){ // must be element?
19814                 id = id.id;
19815             }
19816             return elements[id];
19817         },
19818
19819     /**
19820      * Returns a custom data object that is registered for the DOM node that is the target of the event
19821      * @param {Event} e The event
19822      * @return {Object} data The custom data
19823      */
19824         getTargetFromEvent : function(e){
19825             var t = Roo.lib.Event.getTarget(e);
19826             return t ? elements[t.id] || handles[t.id] : null;
19827         }
19828     };
19829 }();/*
19830  * Based on:
19831  * Ext JS Library 1.1.1
19832  * Copyright(c) 2006-2007, Ext JS, LLC.
19833  *
19834  * Originally Released Under LGPL - original licence link has changed is not relivant.
19835  *
19836  * Fork - LGPL
19837  * <script type="text/javascript">
19838  */
19839  
19840
19841 /**
19842  * @class Roo.dd.StatusProxy
19843  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19844  * default drag proxy used by all Roo.dd components.
19845  * @constructor
19846  * @param {Object} config
19847  */
19848 Roo.dd.StatusProxy = function(config){
19849     Roo.apply(this, config);
19850     this.id = this.id || Roo.id();
19851     this.el = new Roo.Layer({
19852         dh: {
19853             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19854                 {tag: "div", cls: "x-dd-drop-icon"},
19855                 {tag: "div", cls: "x-dd-drag-ghost"}
19856             ]
19857         }, 
19858         shadow: !config || config.shadow !== false
19859     });
19860     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19861     this.dropStatus = this.dropNotAllowed;
19862 };
19863
19864 Roo.dd.StatusProxy.prototype = {
19865     /**
19866      * @cfg {String} dropAllowed
19867      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19868      */
19869     dropAllowed : "x-dd-drop-ok",
19870     /**
19871      * @cfg {String} dropNotAllowed
19872      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19873      */
19874     dropNotAllowed : "x-dd-drop-nodrop",
19875
19876     /**
19877      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19878      * over the current target element.
19879      * @param {String} cssClass The css class for the new drop status indicator image
19880      */
19881     setStatus : function(cssClass){
19882         cssClass = cssClass || this.dropNotAllowed;
19883         if(this.dropStatus != cssClass){
19884             this.el.replaceClass(this.dropStatus, cssClass);
19885             this.dropStatus = cssClass;
19886         }
19887     },
19888
19889     /**
19890      * Resets the status indicator to the default dropNotAllowed value
19891      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19892      */
19893     reset : function(clearGhost){
19894         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19895         this.dropStatus = this.dropNotAllowed;
19896         if(clearGhost){
19897             this.ghost.update("");
19898         }
19899     },
19900
19901     /**
19902      * Updates the contents of the ghost element
19903      * @param {String} html The html that will replace the current innerHTML of the ghost element
19904      */
19905     update : function(html){
19906         if(typeof html == "string"){
19907             this.ghost.update(html);
19908         }else{
19909             this.ghost.update("");
19910             html.style.margin = "0";
19911             this.ghost.dom.appendChild(html);
19912         }
19913         // ensure float = none set?? cant remember why though.
19914         var el = this.ghost.dom.firstChild;
19915                 if(el){
19916                         Roo.fly(el).setStyle('float', 'none');
19917                 }
19918     },
19919     
19920     /**
19921      * Returns the underlying proxy {@link Roo.Layer}
19922      * @return {Roo.Layer} el
19923     */
19924     getEl : function(){
19925         return this.el;
19926     },
19927
19928     /**
19929      * Returns the ghost element
19930      * @return {Roo.Element} el
19931      */
19932     getGhost : function(){
19933         return this.ghost;
19934     },
19935
19936     /**
19937      * Hides the proxy
19938      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19939      */
19940     hide : function(clear){
19941         this.el.hide();
19942         if(clear){
19943             this.reset(true);
19944         }
19945     },
19946
19947     /**
19948      * Stops the repair animation if it's currently running
19949      */
19950     stop : function(){
19951         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19952             this.anim.stop();
19953         }
19954     },
19955
19956     /**
19957      * Displays this proxy
19958      */
19959     show : function(){
19960         this.el.show();
19961     },
19962
19963     /**
19964      * Force the Layer to sync its shadow and shim positions to the element
19965      */
19966     sync : function(){
19967         this.el.sync();
19968     },
19969
19970     /**
19971      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19972      * invalid drop operation by the item being dragged.
19973      * @param {Array} xy The XY position of the element ([x, y])
19974      * @param {Function} callback The function to call after the repair is complete
19975      * @param {Object} scope The scope in which to execute the callback
19976      */
19977     repair : function(xy, callback, scope){
19978         this.callback = callback;
19979         this.scope = scope;
19980         if(xy && this.animRepair !== false){
19981             this.el.addClass("x-dd-drag-repair");
19982             this.el.hideUnders(true);
19983             this.anim = this.el.shift({
19984                 duration: this.repairDuration || .5,
19985                 easing: 'easeOut',
19986                 xy: xy,
19987                 stopFx: true,
19988                 callback: this.afterRepair,
19989                 scope: this
19990             });
19991         }else{
19992             this.afterRepair();
19993         }
19994     },
19995
19996     // private
19997     afterRepair : function(){
19998         this.hide(true);
19999         if(typeof this.callback == "function"){
20000             this.callback.call(this.scope || this);
20001         }
20002         this.callback = null;
20003         this.scope = null;
20004     }
20005 };/*
20006  * Based on:
20007  * Ext JS Library 1.1.1
20008  * Copyright(c) 2006-2007, Ext JS, LLC.
20009  *
20010  * Originally Released Under LGPL - original licence link has changed is not relivant.
20011  *
20012  * Fork - LGPL
20013  * <script type="text/javascript">
20014  */
20015
20016 /**
20017  * @class Roo.dd.DragSource
20018  * @extends Roo.dd.DDProxy
20019  * A simple class that provides the basic implementation needed to make any element draggable.
20020  * @constructor
20021  * @param {String/HTMLElement/Element} el The container element
20022  * @param {Object} config
20023  */
20024 Roo.dd.DragSource = function(el, config){
20025     this.el = Roo.get(el);
20026     this.dragData = {};
20027     
20028     Roo.apply(this, config);
20029     
20030     if(!this.proxy){
20031         this.proxy = new Roo.dd.StatusProxy();
20032     }
20033
20034     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20035           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20036     
20037     this.dragging = false;
20038 };
20039
20040 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20041     /**
20042      * @cfg {String} dropAllowed
20043      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20044      */
20045     dropAllowed : "x-dd-drop-ok",
20046     /**
20047      * @cfg {String} dropNotAllowed
20048      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20049      */
20050     dropNotAllowed : "x-dd-drop-nodrop",
20051
20052     /**
20053      * Returns the data object associated with this drag source
20054      * @return {Object} data An object containing arbitrary data
20055      */
20056     getDragData : function(e){
20057         return this.dragData;
20058     },
20059
20060     // private
20061     onDragEnter : function(e, id){
20062         var target = Roo.dd.DragDropMgr.getDDById(id);
20063         this.cachedTarget = target;
20064         if(this.beforeDragEnter(target, e, id) !== false){
20065             if(target.isNotifyTarget){
20066                 var status = target.notifyEnter(this, e, this.dragData);
20067                 this.proxy.setStatus(status);
20068             }else{
20069                 this.proxy.setStatus(this.dropAllowed);
20070             }
20071             
20072             if(this.afterDragEnter){
20073                 /**
20074                  * An empty function by default, but provided so that you can perform a custom action
20075                  * when the dragged item enters the drop target by providing an implementation.
20076                  * @param {Roo.dd.DragDrop} target The drop target
20077                  * @param {Event} e The event object
20078                  * @param {String} id The id of the dragged element
20079                  * @method afterDragEnter
20080                  */
20081                 this.afterDragEnter(target, e, id);
20082             }
20083         }
20084     },
20085
20086     /**
20087      * An empty function by default, but provided so that you can perform a custom action
20088      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20089      * @param {Roo.dd.DragDrop} target The drop target
20090      * @param {Event} e The event object
20091      * @param {String} id The id of the dragged element
20092      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20093      */
20094     beforeDragEnter : function(target, e, id){
20095         return true;
20096     },
20097
20098     // private
20099     alignElWithMouse: function() {
20100         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20101         this.proxy.sync();
20102     },
20103
20104     // private
20105     onDragOver : function(e, id){
20106         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20107         if(this.beforeDragOver(target, e, id) !== false){
20108             if(target.isNotifyTarget){
20109                 var status = target.notifyOver(this, e, this.dragData);
20110                 this.proxy.setStatus(status);
20111             }
20112
20113             if(this.afterDragOver){
20114                 /**
20115                  * An empty function by default, but provided so that you can perform a custom action
20116                  * while the dragged item is over the drop target by providing an implementation.
20117                  * @param {Roo.dd.DragDrop} target The drop target
20118                  * @param {Event} e The event object
20119                  * @param {String} id The id of the dragged element
20120                  * @method afterDragOver
20121                  */
20122                 this.afterDragOver(target, e, id);
20123             }
20124         }
20125     },
20126
20127     /**
20128      * An empty function by default, but provided so that you can perform a custom action
20129      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20130      * @param {Roo.dd.DragDrop} target The drop target
20131      * @param {Event} e The event object
20132      * @param {String} id The id of the dragged element
20133      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20134      */
20135     beforeDragOver : function(target, e, id){
20136         return true;
20137     },
20138
20139     // private
20140     onDragOut : function(e, id){
20141         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20142         if(this.beforeDragOut(target, e, id) !== false){
20143             if(target.isNotifyTarget){
20144                 target.notifyOut(this, e, this.dragData);
20145             }
20146             this.proxy.reset();
20147             if(this.afterDragOut){
20148                 /**
20149                  * An empty function by default, but provided so that you can perform a custom action
20150                  * after the dragged item is dragged out of the target without dropping.
20151                  * @param {Roo.dd.DragDrop} target The drop target
20152                  * @param {Event} e The event object
20153                  * @param {String} id The id of the dragged element
20154                  * @method afterDragOut
20155                  */
20156                 this.afterDragOut(target, e, id);
20157             }
20158         }
20159         this.cachedTarget = null;
20160     },
20161
20162     /**
20163      * An empty function by default, but provided so that you can perform a custom action before the dragged
20164      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20165      * @param {Roo.dd.DragDrop} target The drop target
20166      * @param {Event} e The event object
20167      * @param {String} id The id of the dragged element
20168      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20169      */
20170     beforeDragOut : function(target, e, id){
20171         return true;
20172     },
20173     
20174     // private
20175     onDragDrop : function(e, id){
20176         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20177         if(this.beforeDragDrop(target, e, id) !== false){
20178             if(target.isNotifyTarget){
20179                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20180                     this.onValidDrop(target, e, id);
20181                 }else{
20182                     this.onInvalidDrop(target, e, id);
20183                 }
20184             }else{
20185                 this.onValidDrop(target, e, id);
20186             }
20187             
20188             if(this.afterDragDrop){
20189                 /**
20190                  * An empty function by default, but provided so that you can perform a custom action
20191                  * after a valid drag drop has occurred by providing an implementation.
20192                  * @param {Roo.dd.DragDrop} target The drop target
20193                  * @param {Event} e The event object
20194                  * @param {String} id The id of the dropped element
20195                  * @method afterDragDrop
20196                  */
20197                 this.afterDragDrop(target, e, id);
20198             }
20199         }
20200         delete this.cachedTarget;
20201     },
20202
20203     /**
20204      * An empty function by default, but provided so that you can perform a custom action before the dragged
20205      * item is dropped onto the target and optionally cancel the onDragDrop.
20206      * @param {Roo.dd.DragDrop} target The drop target
20207      * @param {Event} e The event object
20208      * @param {String} id The id of the dragged element
20209      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20210      */
20211     beforeDragDrop : function(target, e, id){
20212         return true;
20213     },
20214
20215     // private
20216     onValidDrop : function(target, e, id){
20217         this.hideProxy();
20218         if(this.afterValidDrop){
20219             /**
20220              * An empty function by default, but provided so that you can perform a custom action
20221              * after a valid drop has occurred by providing an implementation.
20222              * @param {Object} target The target DD 
20223              * @param {Event} e The event object
20224              * @param {String} id The id of the dropped element
20225              * @method afterInvalidDrop
20226              */
20227             this.afterValidDrop(target, e, id);
20228         }
20229     },
20230
20231     // private
20232     getRepairXY : function(e, data){
20233         return this.el.getXY();  
20234     },
20235
20236     // private
20237     onInvalidDrop : function(target, e, id){
20238         this.beforeInvalidDrop(target, e, id);
20239         if(this.cachedTarget){
20240             if(this.cachedTarget.isNotifyTarget){
20241                 this.cachedTarget.notifyOut(this, e, this.dragData);
20242             }
20243             this.cacheTarget = null;
20244         }
20245         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20246
20247         if(this.afterInvalidDrop){
20248             /**
20249              * An empty function by default, but provided so that you can perform a custom action
20250              * after an invalid drop has occurred by providing an implementation.
20251              * @param {Event} e The event object
20252              * @param {String} id The id of the dropped element
20253              * @method afterInvalidDrop
20254              */
20255             this.afterInvalidDrop(e, id);
20256         }
20257     },
20258
20259     // private
20260     afterRepair : function(){
20261         if(Roo.enableFx){
20262             this.el.highlight(this.hlColor || "c3daf9");
20263         }
20264         this.dragging = false;
20265     },
20266
20267     /**
20268      * An empty function by default, but provided so that you can perform a custom action after an invalid
20269      * drop has occurred.
20270      * @param {Roo.dd.DragDrop} target The drop target
20271      * @param {Event} e The event object
20272      * @param {String} id The id of the dragged element
20273      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20274      */
20275     beforeInvalidDrop : function(target, e, id){
20276         return true;
20277     },
20278
20279     // private
20280     handleMouseDown : function(e){
20281         if(this.dragging) {
20282             return;
20283         }
20284         var data = this.getDragData(e);
20285         if(data && this.onBeforeDrag(data, e) !== false){
20286             this.dragData = data;
20287             this.proxy.stop();
20288             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20289         } 
20290     },
20291
20292     /**
20293      * An empty function by default, but provided so that you can perform a custom action before the initial
20294      * drag event begins and optionally cancel it.
20295      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20296      * @param {Event} e The event object
20297      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20298      */
20299     onBeforeDrag : function(data, e){
20300         return true;
20301     },
20302
20303     /**
20304      * An empty function by default, but provided so that you can perform a custom action once the initial
20305      * drag event has begun.  The drag cannot be canceled from this function.
20306      * @param {Number} x The x position of the click on the dragged object
20307      * @param {Number} y The y position of the click on the dragged object
20308      */
20309     onStartDrag : Roo.emptyFn,
20310
20311     // private - YUI override
20312     startDrag : function(x, y){
20313         this.proxy.reset();
20314         this.dragging = true;
20315         this.proxy.update("");
20316         this.onInitDrag(x, y);
20317         this.proxy.show();
20318     },
20319
20320     // private
20321     onInitDrag : function(x, y){
20322         var clone = this.el.dom.cloneNode(true);
20323         clone.id = Roo.id(); // prevent duplicate ids
20324         this.proxy.update(clone);
20325         this.onStartDrag(x, y);
20326         return true;
20327     },
20328
20329     /**
20330      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20331      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20332      */
20333     getProxy : function(){
20334         return this.proxy;  
20335     },
20336
20337     /**
20338      * Hides the drag source's {@link Roo.dd.StatusProxy}
20339      */
20340     hideProxy : function(){
20341         this.proxy.hide();  
20342         this.proxy.reset(true);
20343         this.dragging = false;
20344     },
20345
20346     // private
20347     triggerCacheRefresh : function(){
20348         Roo.dd.DDM.refreshCache(this.groups);
20349     },
20350
20351     // private - override to prevent hiding
20352     b4EndDrag: function(e) {
20353     },
20354
20355     // private - override to prevent moving
20356     endDrag : function(e){
20357         this.onEndDrag(this.dragData, e);
20358     },
20359
20360     // private
20361     onEndDrag : function(data, e){
20362     },
20363     
20364     // private - pin to cursor
20365     autoOffset : function(x, y) {
20366         this.setDelta(-12, -20);
20367     }    
20368 });/*
20369  * Based on:
20370  * Ext JS Library 1.1.1
20371  * Copyright(c) 2006-2007, Ext JS, LLC.
20372  *
20373  * Originally Released Under LGPL - original licence link has changed is not relivant.
20374  *
20375  * Fork - LGPL
20376  * <script type="text/javascript">
20377  */
20378
20379
20380 /**
20381  * @class Roo.dd.DropTarget
20382  * @extends Roo.dd.DDTarget
20383  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20384  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20385  * @constructor
20386  * @param {String/HTMLElement/Element} el The container element
20387  * @param {Object} config
20388  */
20389 Roo.dd.DropTarget = function(el, config){
20390     this.el = Roo.get(el);
20391     
20392     var listeners = false; ;
20393     if (config && config.listeners) {
20394         listeners= config.listeners;
20395         delete config.listeners;
20396     }
20397     Roo.apply(this, config);
20398     
20399     if(this.containerScroll){
20400         Roo.dd.ScrollManager.register(this.el);
20401     }
20402     this.addEvents( {
20403          /**
20404          * @scope Roo.dd.DropTarget
20405          */
20406          
20407          /**
20408          * @event enter
20409          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20410          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20411          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20412          * 
20413          * IMPORTANT : it should set this.overClass and this.dropAllowed
20414          * 
20415          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20416          * @param {Event} e The event
20417          * @param {Object} data An object containing arbitrary data supplied by the drag source
20418          */
20419         "enter" : true,
20420         
20421          /**
20422          * @event over
20423          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20424          * This method will be called on every mouse movement while the drag source is over the drop target.
20425          * This default implementation simply returns the dropAllowed config value.
20426          * 
20427          * IMPORTANT : it should set this.dropAllowed
20428          * 
20429          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20430          * @param {Event} e The event
20431          * @param {Object} data An object containing arbitrary data supplied by the drag source
20432          
20433          */
20434         "over" : true,
20435         /**
20436          * @event out
20437          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20438          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20439          * overClass (if any) from the drop element.
20440          * 
20441          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20442          * @param {Event} e The event
20443          * @param {Object} data An object containing arbitrary data supplied by the drag source
20444          */
20445          "out" : true,
20446          
20447         /**
20448          * @event drop
20449          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20450          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20451          * implementation that does something to process the drop event and returns true so that the drag source's
20452          * repair action does not run.
20453          * 
20454          * IMPORTANT : it should set this.success
20455          * 
20456          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20457          * @param {Event} e The event
20458          * @param {Object} data An object containing arbitrary data supplied by the drag source
20459         */
20460          "drop" : true
20461     });
20462             
20463      
20464     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20465         this.el.dom, 
20466         this.ddGroup || this.group,
20467         {
20468             isTarget: true,
20469             listeners : listeners || {} 
20470            
20471         
20472         }
20473     );
20474
20475 };
20476
20477 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20478     /**
20479      * @cfg {String} overClass
20480      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20481      */
20482      /**
20483      * @cfg {String} ddGroup
20484      * The drag drop group to handle drop events for
20485      */
20486      
20487     /**
20488      * @cfg {String} dropAllowed
20489      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20490      */
20491     dropAllowed : "x-dd-drop-ok",
20492     /**
20493      * @cfg {String} dropNotAllowed
20494      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20495      */
20496     dropNotAllowed : "x-dd-drop-nodrop",
20497     /**
20498      * @cfg {boolean} success
20499      * set this after drop listener.. 
20500      */
20501     success : false,
20502     /**
20503      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20504      * if the drop point is valid for over/enter..
20505      */
20506     valid : false,
20507     // private
20508     isTarget : true,
20509
20510     // private
20511     isNotifyTarget : true,
20512     
20513     /**
20514      * @hide
20515      */
20516     notifyEnter : function(dd, e, data)
20517     {
20518         this.valid = true;
20519         this.fireEvent('enter', dd, e, data);
20520         if(this.overClass){
20521             this.el.addClass(this.overClass);
20522         }
20523         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20524             this.valid ? this.dropAllowed : this.dropNotAllowed
20525         );
20526     },
20527
20528     /**
20529      * @hide
20530      */
20531     notifyOver : function(dd, e, data)
20532     {
20533         this.valid = true;
20534         this.fireEvent('over', dd, e, data);
20535         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20536             this.valid ? this.dropAllowed : this.dropNotAllowed
20537         );
20538     },
20539
20540     /**
20541      * @hide
20542      */
20543     notifyOut : function(dd, e, data)
20544     {
20545         this.fireEvent('out', dd, e, data);
20546         if(this.overClass){
20547             this.el.removeClass(this.overClass);
20548         }
20549     },
20550
20551     /**
20552      * @hide
20553      */
20554     notifyDrop : function(dd, e, data)
20555     {
20556         this.success = false;
20557         this.fireEvent('drop', dd, e, data);
20558         return this.success;
20559     }
20560 });/*
20561  * Based on:
20562  * Ext JS Library 1.1.1
20563  * Copyright(c) 2006-2007, Ext JS, LLC.
20564  *
20565  * Originally Released Under LGPL - original licence link has changed is not relivant.
20566  *
20567  * Fork - LGPL
20568  * <script type="text/javascript">
20569  */
20570
20571
20572 /**
20573  * @class Roo.dd.DragZone
20574  * @extends Roo.dd.DragSource
20575  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20576  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20577  * @constructor
20578  * @param {String/HTMLElement/Element} el The container element
20579  * @param {Object} config
20580  */
20581 Roo.dd.DragZone = function(el, config){
20582     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20583     if(this.containerScroll){
20584         Roo.dd.ScrollManager.register(this.el);
20585     }
20586 };
20587
20588 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20589     /**
20590      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20591      * for auto scrolling during drag operations.
20592      */
20593     /**
20594      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20595      * method after a failed drop (defaults to "c3daf9" - light blue)
20596      */
20597
20598     /**
20599      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20600      * for a valid target to drag based on the mouse down. Override this method
20601      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20602      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20603      * @param {EventObject} e The mouse down event
20604      * @return {Object} The dragData
20605      */
20606     getDragData : function(e){
20607         return Roo.dd.Registry.getHandleFromEvent(e);
20608     },
20609     
20610     /**
20611      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20612      * this.dragData.ddel
20613      * @param {Number} x The x position of the click on the dragged object
20614      * @param {Number} y The y position of the click on the dragged object
20615      * @return {Boolean} true to continue the drag, false to cancel
20616      */
20617     onInitDrag : function(x, y){
20618         this.proxy.update(this.dragData.ddel.cloneNode(true));
20619         this.onStartDrag(x, y);
20620         return true;
20621     },
20622     
20623     /**
20624      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20625      */
20626     afterRepair : function(){
20627         if(Roo.enableFx){
20628             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20629         }
20630         this.dragging = false;
20631     },
20632
20633     /**
20634      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20635      * the XY of this.dragData.ddel
20636      * @param {EventObject} e The mouse up event
20637      * @return {Array} The xy location (e.g. [100, 200])
20638      */
20639     getRepairXY : function(e){
20640         return Roo.Element.fly(this.dragData.ddel).getXY();  
20641     }
20642 });/*
20643  * Based on:
20644  * Ext JS Library 1.1.1
20645  * Copyright(c) 2006-2007, Ext JS, LLC.
20646  *
20647  * Originally Released Under LGPL - original licence link has changed is not relivant.
20648  *
20649  * Fork - LGPL
20650  * <script type="text/javascript">
20651  */
20652 /**
20653  * @class Roo.dd.DropZone
20654  * @extends Roo.dd.DropTarget
20655  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20656  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20657  * @constructor
20658  * @param {String/HTMLElement/Element} el The container element
20659  * @param {Object} config
20660  */
20661 Roo.dd.DropZone = function(el, config){
20662     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20663 };
20664
20665 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20666     /**
20667      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20668      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20669      * provide your own custom lookup.
20670      * @param {Event} e The event
20671      * @return {Object} data The custom data
20672      */
20673     getTargetFromEvent : function(e){
20674         return Roo.dd.Registry.getTargetFromEvent(e);
20675     },
20676
20677     /**
20678      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20679      * that it has registered.  This method has no default implementation and should be overridden to provide
20680      * node-specific processing if necessary.
20681      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20682      * {@link #getTargetFromEvent} for this node)
20683      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20684      * @param {Event} e The event
20685      * @param {Object} data An object containing arbitrary data supplied by the drag source
20686      */
20687     onNodeEnter : function(n, dd, e, data){
20688         
20689     },
20690
20691     /**
20692      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20693      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20694      * overridden to provide the proper feedback.
20695      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20696      * {@link #getTargetFromEvent} for this node)
20697      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20698      * @param {Event} e The event
20699      * @param {Object} data An object containing arbitrary data supplied by the drag source
20700      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20701      * underlying {@link Roo.dd.StatusProxy} can be updated
20702      */
20703     onNodeOver : function(n, dd, e, data){
20704         return this.dropAllowed;
20705     },
20706
20707     /**
20708      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20709      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20710      * node-specific processing if necessary.
20711      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20712      * {@link #getTargetFromEvent} for this node)
20713      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20714      * @param {Event} e The event
20715      * @param {Object} data An object containing arbitrary data supplied by the drag source
20716      */
20717     onNodeOut : function(n, dd, e, data){
20718         
20719     },
20720
20721     /**
20722      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20723      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20724      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20725      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20726      * {@link #getTargetFromEvent} for this node)
20727      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20728      * @param {Event} e The event
20729      * @param {Object} data An object containing arbitrary data supplied by the drag source
20730      * @return {Boolean} True if the drop was valid, else false
20731      */
20732     onNodeDrop : function(n, dd, e, data){
20733         return false;
20734     },
20735
20736     /**
20737      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20738      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20739      * it should be overridden to provide the proper feedback if necessary.
20740      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20741      * @param {Event} e The event
20742      * @param {Object} data An object containing arbitrary data supplied by the drag source
20743      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20744      * underlying {@link Roo.dd.StatusProxy} can be updated
20745      */
20746     onContainerOver : function(dd, e, data){
20747         return this.dropNotAllowed;
20748     },
20749
20750     /**
20751      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20752      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20753      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20754      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20755      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20756      * @param {Event} e The event
20757      * @param {Object} data An object containing arbitrary data supplied by the drag source
20758      * @return {Boolean} True if the drop was valid, else false
20759      */
20760     onContainerDrop : function(dd, e, data){
20761         return false;
20762     },
20763
20764     /**
20765      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20766      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20767      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20768      * you should override this method and provide a custom implementation.
20769      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20770      * @param {Event} e The event
20771      * @param {Object} data An object containing arbitrary data supplied by the drag source
20772      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20773      * underlying {@link Roo.dd.StatusProxy} can be updated
20774      */
20775     notifyEnter : function(dd, e, data){
20776         return this.dropNotAllowed;
20777     },
20778
20779     /**
20780      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20781      * This method will be called on every mouse movement while the drag source is over the drop zone.
20782      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20783      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20784      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20785      * registered node, it will call {@link #onContainerOver}.
20786      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20787      * @param {Event} e The event
20788      * @param {Object} data An object containing arbitrary data supplied by the drag source
20789      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20790      * underlying {@link Roo.dd.StatusProxy} can be updated
20791      */
20792     notifyOver : function(dd, e, data){
20793         var n = this.getTargetFromEvent(e);
20794         if(!n){ // not over valid drop target
20795             if(this.lastOverNode){
20796                 this.onNodeOut(this.lastOverNode, dd, e, data);
20797                 this.lastOverNode = null;
20798             }
20799             return this.onContainerOver(dd, e, data);
20800         }
20801         if(this.lastOverNode != n){
20802             if(this.lastOverNode){
20803                 this.onNodeOut(this.lastOverNode, dd, e, data);
20804             }
20805             this.onNodeEnter(n, dd, e, data);
20806             this.lastOverNode = n;
20807         }
20808         return this.onNodeOver(n, dd, e, data);
20809     },
20810
20811     /**
20812      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20813      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20814      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20815      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20816      * @param {Event} e The event
20817      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20818      */
20819     notifyOut : function(dd, e, data){
20820         if(this.lastOverNode){
20821             this.onNodeOut(this.lastOverNode, dd, e, data);
20822             this.lastOverNode = null;
20823         }
20824     },
20825
20826     /**
20827      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20828      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20829      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20830      * otherwise it will call {@link #onContainerDrop}.
20831      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20832      * @param {Event} e The event
20833      * @param {Object} data An object containing arbitrary data supplied by the drag source
20834      * @return {Boolean} True if the drop was valid, else false
20835      */
20836     notifyDrop : function(dd, e, data){
20837         if(this.lastOverNode){
20838             this.onNodeOut(this.lastOverNode, dd, e, data);
20839             this.lastOverNode = null;
20840         }
20841         var n = this.getTargetFromEvent(e);
20842         return n ?
20843             this.onNodeDrop(n, dd, e, data) :
20844             this.onContainerDrop(dd, e, data);
20845     },
20846
20847     // private
20848     triggerCacheRefresh : function(){
20849         Roo.dd.DDM.refreshCache(this.groups);
20850     }  
20851 });/*
20852  * Based on:
20853  * Ext JS Library 1.1.1
20854  * Copyright(c) 2006-2007, Ext JS, LLC.
20855  *
20856  * Originally Released Under LGPL - original licence link has changed is not relivant.
20857  *
20858  * Fork - LGPL
20859  * <script type="text/javascript">
20860  */
20861
20862
20863 /**
20864  * @class Roo.data.SortTypes
20865  * @singleton
20866  * Defines the default sorting (casting?) comparison functions used when sorting data.
20867  */
20868 Roo.data.SortTypes = {
20869     /**
20870      * Default sort that does nothing
20871      * @param {Mixed} s The value being converted
20872      * @return {Mixed} The comparison value
20873      */
20874     none : function(s){
20875         return s;
20876     },
20877     
20878     /**
20879      * The regular expression used to strip tags
20880      * @type {RegExp}
20881      * @property
20882      */
20883     stripTagsRE : /<\/?[^>]+>/gi,
20884     
20885     /**
20886      * Strips all HTML tags to sort on text only
20887      * @param {Mixed} s The value being converted
20888      * @return {String} The comparison value
20889      */
20890     asText : function(s){
20891         return String(s).replace(this.stripTagsRE, "");
20892     },
20893     
20894     /**
20895      * Strips all HTML tags to sort on text only - Case insensitive
20896      * @param {Mixed} s The value being converted
20897      * @return {String} The comparison value
20898      */
20899     asUCText : function(s){
20900         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20901     },
20902     
20903     /**
20904      * Case insensitive string
20905      * @param {Mixed} s The value being converted
20906      * @return {String} The comparison value
20907      */
20908     asUCString : function(s) {
20909         return String(s).toUpperCase();
20910     },
20911     
20912     /**
20913      * Date sorting
20914      * @param {Mixed} s The value being converted
20915      * @return {Number} The comparison value
20916      */
20917     asDate : function(s) {
20918         if(!s){
20919             return 0;
20920         }
20921         if(s instanceof Date){
20922             return s.getTime();
20923         }
20924         return Date.parse(String(s));
20925     },
20926     
20927     /**
20928      * Float sorting
20929      * @param {Mixed} s The value being converted
20930      * @return {Float} The comparison value
20931      */
20932     asFloat : function(s) {
20933         var val = parseFloat(String(s).replace(/,/g, ""));
20934         if(isNaN(val)) {
20935             val = 0;
20936         }
20937         return val;
20938     },
20939     
20940     /**
20941      * Integer sorting
20942      * @param {Mixed} s The value being converted
20943      * @return {Number} The comparison value
20944      */
20945     asInt : function(s) {
20946         var val = parseInt(String(s).replace(/,/g, ""));
20947         if(isNaN(val)) {
20948             val = 0;
20949         }
20950         return val;
20951     }
20952 };/*
20953  * Based on:
20954  * Ext JS Library 1.1.1
20955  * Copyright(c) 2006-2007, Ext JS, LLC.
20956  *
20957  * Originally Released Under LGPL - original licence link has changed is not relivant.
20958  *
20959  * Fork - LGPL
20960  * <script type="text/javascript">
20961  */
20962
20963 /**
20964 * @class Roo.data.Record
20965  * Instances of this class encapsulate both record <em>definition</em> information, and record
20966  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20967  * to access Records cached in an {@link Roo.data.Store} object.<br>
20968  * <p>
20969  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20970  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20971  * objects.<br>
20972  * <p>
20973  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20974  * @constructor
20975  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20976  * {@link #create}. The parameters are the same.
20977  * @param {Array} data An associative Array of data values keyed by the field name.
20978  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20979  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20980  * not specified an integer id is generated.
20981  */
20982 Roo.data.Record = function(data, id){
20983     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20984     this.data = data;
20985 };
20986
20987 /**
20988  * Generate a constructor for a specific record layout.
20989  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20990  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20991  * Each field definition object may contain the following properties: <ul>
20992  * <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,
20993  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20994  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20995  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20996  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20997  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20998  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20999  * this may be omitted.</p></li>
21000  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
21001  * <ul><li>auto (Default, implies no conversion)</li>
21002  * <li>string</li>
21003  * <li>int</li>
21004  * <li>float</li>
21005  * <li>boolean</li>
21006  * <li>date</li></ul></p></li>
21007  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
21008  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
21009  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
21010  * by the Reader into an object that will be stored in the Record. It is passed the
21011  * following parameters:<ul>
21012  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
21013  * </ul></p></li>
21014  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
21015  * </ul>
21016  * <br>usage:<br><pre><code>
21017 var TopicRecord = Roo.data.Record.create(
21018     {name: 'title', mapping: 'topic_title'},
21019     {name: 'author', mapping: 'username'},
21020     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
21021     {name: 'lastPost', mapping: 'post_time', type: 'date'},
21022     {name: 'lastPoster', mapping: 'user2'},
21023     {name: 'excerpt', mapping: 'post_text'}
21024 );
21025
21026 var myNewRecord = new TopicRecord({
21027     title: 'Do my job please',
21028     author: 'noobie',
21029     totalPosts: 1,
21030     lastPost: new Date(),
21031     lastPoster: 'Animal',
21032     excerpt: 'No way dude!'
21033 });
21034 myStore.add(myNewRecord);
21035 </code></pre>
21036  * @method create
21037  * @static
21038  */
21039 Roo.data.Record.create = function(o){
21040     var f = function(){
21041         f.superclass.constructor.apply(this, arguments);
21042     };
21043     Roo.extend(f, Roo.data.Record);
21044     var p = f.prototype;
21045     p.fields = new Roo.util.MixedCollection(false, function(field){
21046         return field.name;
21047     });
21048     for(var i = 0, len = o.length; i < len; i++){
21049         p.fields.add(new Roo.data.Field(o[i]));
21050     }
21051     f.getField = function(name){
21052         return p.fields.get(name);  
21053     };
21054     return f;
21055 };
21056
21057 Roo.data.Record.AUTO_ID = 1000;
21058 Roo.data.Record.EDIT = 'edit';
21059 Roo.data.Record.REJECT = 'reject';
21060 Roo.data.Record.COMMIT = 'commit';
21061
21062 Roo.data.Record.prototype = {
21063     /**
21064      * Readonly flag - true if this record has been modified.
21065      * @type Boolean
21066      */
21067     dirty : false,
21068     editing : false,
21069     error: null,
21070     modified: null,
21071
21072     // private
21073     join : function(store){
21074         this.store = store;
21075     },
21076
21077     /**
21078      * Set the named field to the specified value.
21079      * @param {String} name The name of the field to set.
21080      * @param {Object} value The value to set the field to.
21081      */
21082     set : function(name, value){
21083         if(this.data[name] == value){
21084             return;
21085         }
21086         this.dirty = true;
21087         if(!this.modified){
21088             this.modified = {};
21089         }
21090         if(typeof this.modified[name] == 'undefined'){
21091             this.modified[name] = this.data[name];
21092         }
21093         this.data[name] = value;
21094         if(!this.editing && this.store){
21095             this.store.afterEdit(this);
21096         }       
21097     },
21098
21099     /**
21100      * Get the value of the named field.
21101      * @param {String} name The name of the field to get the value of.
21102      * @return {Object} The value of the field.
21103      */
21104     get : function(name){
21105         return this.data[name]; 
21106     },
21107
21108     // private
21109     beginEdit : function(){
21110         this.editing = true;
21111         this.modified = {}; 
21112     },
21113
21114     // private
21115     cancelEdit : function(){
21116         this.editing = false;
21117         delete this.modified;
21118     },
21119
21120     // private
21121     endEdit : function(){
21122         this.editing = false;
21123         if(this.dirty && this.store){
21124             this.store.afterEdit(this);
21125         }
21126     },
21127
21128     /**
21129      * Usually called by the {@link Roo.data.Store} which owns the Record.
21130      * Rejects all changes made to the Record since either creation, or the last commit operation.
21131      * Modified fields are reverted to their original values.
21132      * <p>
21133      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21134      * of reject operations.
21135      */
21136     reject : function(){
21137         var m = this.modified;
21138         for(var n in m){
21139             if(typeof m[n] != "function"){
21140                 this.data[n] = m[n];
21141             }
21142         }
21143         this.dirty = false;
21144         delete this.modified;
21145         this.editing = false;
21146         if(this.store){
21147             this.store.afterReject(this);
21148         }
21149     },
21150
21151     /**
21152      * Usually called by the {@link Roo.data.Store} which owns the Record.
21153      * Commits all changes made to the Record since either creation, or the last commit operation.
21154      * <p>
21155      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21156      * of commit operations.
21157      */
21158     commit : function(){
21159         this.dirty = false;
21160         delete this.modified;
21161         this.editing = false;
21162         if(this.store){
21163             this.store.afterCommit(this);
21164         }
21165     },
21166
21167     // private
21168     hasError : function(){
21169         return this.error != null;
21170     },
21171
21172     // private
21173     clearError : function(){
21174         this.error = null;
21175     },
21176
21177     /**
21178      * Creates a copy of this record.
21179      * @param {String} id (optional) A new record id if you don't want to use this record's id
21180      * @return {Record}
21181      */
21182     copy : function(newId) {
21183         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21184     }
21185 };/*
21186  * Based on:
21187  * Ext JS Library 1.1.1
21188  * Copyright(c) 2006-2007, Ext JS, LLC.
21189  *
21190  * Originally Released Under LGPL - original licence link has changed is not relivant.
21191  *
21192  * Fork - LGPL
21193  * <script type="text/javascript">
21194  */
21195
21196
21197
21198 /**
21199  * @class Roo.data.Store
21200  * @extends Roo.util.Observable
21201  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21202  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21203  * <p>
21204  * 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
21205  * has no knowledge of the format of the data returned by the Proxy.<br>
21206  * <p>
21207  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21208  * instances from the data object. These records are cached and made available through accessor functions.
21209  * @constructor
21210  * Creates a new Store.
21211  * @param {Object} config A config object containing the objects needed for the Store to access data,
21212  * and read the data into Records.
21213  */
21214 Roo.data.Store = function(config){
21215     this.data = new Roo.util.MixedCollection(false);
21216     this.data.getKey = function(o){
21217         return o.id;
21218     };
21219     this.baseParams = {};
21220     // private
21221     this.paramNames = {
21222         "start" : "start",
21223         "limit" : "limit",
21224         "sort" : "sort",
21225         "dir" : "dir",
21226         "multisort" : "_multisort"
21227     };
21228
21229     if(config && config.data){
21230         this.inlineData = config.data;
21231         delete config.data;
21232     }
21233
21234     Roo.apply(this, config);
21235     
21236     if(this.reader){ // reader passed
21237         this.reader = Roo.factory(this.reader, Roo.data);
21238         this.reader.xmodule = this.xmodule || false;
21239         if(!this.recordType){
21240             this.recordType = this.reader.recordType;
21241         }
21242         if(this.reader.onMetaChange){
21243             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21244         }
21245     }
21246
21247     if(this.recordType){
21248         this.fields = this.recordType.prototype.fields;
21249     }
21250     this.modified = [];
21251
21252     this.addEvents({
21253         /**
21254          * @event datachanged
21255          * Fires when the data cache has changed, and a widget which is using this Store
21256          * as a Record cache should refresh its view.
21257          * @param {Store} this
21258          */
21259         datachanged : true,
21260         /**
21261          * @event metachange
21262          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21263          * @param {Store} this
21264          * @param {Object} meta The JSON metadata
21265          */
21266         metachange : true,
21267         /**
21268          * @event add
21269          * Fires when Records have been added to the Store
21270          * @param {Store} this
21271          * @param {Roo.data.Record[]} records The array of Records added
21272          * @param {Number} index The index at which the record(s) were added
21273          */
21274         add : true,
21275         /**
21276          * @event remove
21277          * Fires when a Record has been removed from the Store
21278          * @param {Store} this
21279          * @param {Roo.data.Record} record The Record that was removed
21280          * @param {Number} index The index at which the record was removed
21281          */
21282         remove : true,
21283         /**
21284          * @event update
21285          * Fires when a Record has been updated
21286          * @param {Store} this
21287          * @param {Roo.data.Record} record The Record that was updated
21288          * @param {String} operation The update operation being performed.  Value may be one of:
21289          * <pre><code>
21290  Roo.data.Record.EDIT
21291  Roo.data.Record.REJECT
21292  Roo.data.Record.COMMIT
21293          * </code></pre>
21294          */
21295         update : true,
21296         /**
21297          * @event clear
21298          * Fires when the data cache has been cleared.
21299          * @param {Store} this
21300          */
21301         clear : true,
21302         /**
21303          * @event beforeload
21304          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21305          * the load action will be canceled.
21306          * @param {Store} this
21307          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21308          */
21309         beforeload : true,
21310         /**
21311          * @event beforeloadadd
21312          * Fires after a new set of Records has been loaded.
21313          * @param {Store} this
21314          * @param {Roo.data.Record[]} records The Records that were loaded
21315          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21316          */
21317         beforeloadadd : true,
21318         /**
21319          * @event load
21320          * Fires after a new set of Records has been loaded, before they are added to the store.
21321          * @param {Store} this
21322          * @param {Roo.data.Record[]} records The Records that were loaded
21323          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21324          * @params {Object} return from reader
21325          */
21326         load : true,
21327         /**
21328          * @event loadexception
21329          * Fires if an exception occurs in the Proxy during loading.
21330          * Called with the signature of the Proxy's "loadexception" event.
21331          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21332          * 
21333          * @param {Proxy} 
21334          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21335          * @param {Object} load options 
21336          * @param {Object} jsonData from your request (normally this contains the Exception)
21337          */
21338         loadexception : true
21339     });
21340     
21341     if(this.proxy){
21342         this.proxy = Roo.factory(this.proxy, Roo.data);
21343         this.proxy.xmodule = this.xmodule || false;
21344         this.relayEvents(this.proxy,  ["loadexception"]);
21345     }
21346     this.sortToggle = {};
21347     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21348
21349     Roo.data.Store.superclass.constructor.call(this);
21350
21351     if(this.inlineData){
21352         this.loadData(this.inlineData);
21353         delete this.inlineData;
21354     }
21355 };
21356
21357 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21358      /**
21359     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21360     * without a remote query - used by combo/forms at present.
21361     */
21362     
21363     /**
21364     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21365     */
21366     /**
21367     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21368     */
21369     /**
21370     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21371     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21372     */
21373     /**
21374     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21375     * on any HTTP request
21376     */
21377     /**
21378     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21379     */
21380     /**
21381     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21382     */
21383     multiSort: false,
21384     /**
21385     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21386     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21387     */
21388     remoteSort : false,
21389
21390     /**
21391     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21392      * loaded or when a record is removed. (defaults to false).
21393     */
21394     pruneModifiedRecords : false,
21395
21396     // private
21397     lastOptions : null,
21398
21399     /**
21400      * Add Records to the Store and fires the add event.
21401      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21402      */
21403     add : function(records){
21404         records = [].concat(records);
21405         for(var i = 0, len = records.length; i < len; i++){
21406             records[i].join(this);
21407         }
21408         var index = this.data.length;
21409         this.data.addAll(records);
21410         this.fireEvent("add", this, records, index);
21411     },
21412
21413     /**
21414      * Remove a Record from the Store and fires the remove event.
21415      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21416      */
21417     remove : function(record){
21418         var index = this.data.indexOf(record);
21419         this.data.removeAt(index);
21420         if(this.pruneModifiedRecords){
21421             this.modified.remove(record);
21422         }
21423         this.fireEvent("remove", this, record, index);
21424     },
21425
21426     /**
21427      * Remove all Records from the Store and fires the clear event.
21428      */
21429     removeAll : function(){
21430         this.data.clear();
21431         if(this.pruneModifiedRecords){
21432             this.modified = [];
21433         }
21434         this.fireEvent("clear", this);
21435     },
21436
21437     /**
21438      * Inserts Records to the Store at the given index and fires the add event.
21439      * @param {Number} index The start index at which to insert the passed Records.
21440      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21441      */
21442     insert : function(index, records){
21443         records = [].concat(records);
21444         for(var i = 0, len = records.length; i < len; i++){
21445             this.data.insert(index, records[i]);
21446             records[i].join(this);
21447         }
21448         this.fireEvent("add", this, records, index);
21449     },
21450
21451     /**
21452      * Get the index within the cache of the passed Record.
21453      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21454      * @return {Number} The index of the passed Record. Returns -1 if not found.
21455      */
21456     indexOf : function(record){
21457         return this.data.indexOf(record);
21458     },
21459
21460     /**
21461      * Get the index within the cache of the Record with the passed id.
21462      * @param {String} id The id of the Record to find.
21463      * @return {Number} The index of the Record. Returns -1 if not found.
21464      */
21465     indexOfId : function(id){
21466         return this.data.indexOfKey(id);
21467     },
21468
21469     /**
21470      * Get the Record with the specified id.
21471      * @param {String} id The id of the Record to find.
21472      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21473      */
21474     getById : function(id){
21475         return this.data.key(id);
21476     },
21477
21478     /**
21479      * Get the Record at the specified index.
21480      * @param {Number} index The index of the Record to find.
21481      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21482      */
21483     getAt : function(index){
21484         return this.data.itemAt(index);
21485     },
21486
21487     /**
21488      * Returns a range of Records between specified indices.
21489      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21490      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21491      * @return {Roo.data.Record[]} An array of Records
21492      */
21493     getRange : function(start, end){
21494         return this.data.getRange(start, end);
21495     },
21496
21497     // private
21498     storeOptions : function(o){
21499         o = Roo.apply({}, o);
21500         delete o.callback;
21501         delete o.scope;
21502         this.lastOptions = o;
21503     },
21504
21505     /**
21506      * Loads the Record cache from the configured Proxy using the configured Reader.
21507      * <p>
21508      * If using remote paging, then the first load call must specify the <em>start</em>
21509      * and <em>limit</em> properties in the options.params property to establish the initial
21510      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21511      * <p>
21512      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21513      * and this call will return before the new data has been loaded. Perform any post-processing
21514      * in a callback function, or in a "load" event handler.</strong>
21515      * <p>
21516      * @param {Object} options An object containing properties which control loading options:<ul>
21517      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21518      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21519      * passed the following arguments:<ul>
21520      * <li>r : Roo.data.Record[]</li>
21521      * <li>options: Options object from the load call</li>
21522      * <li>success: Boolean success indicator</li></ul></li>
21523      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21524      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21525      * </ul>
21526      */
21527     load : function(options){
21528         options = options || {};
21529         if(this.fireEvent("beforeload", this, options) !== false){
21530             this.storeOptions(options);
21531             var p = Roo.apply(options.params || {}, this.baseParams);
21532             // if meta was not loaded from remote source.. try requesting it.
21533             if (!this.reader.metaFromRemote) {
21534                 p._requestMeta = 1;
21535             }
21536             if(this.sortInfo && this.remoteSort){
21537                 var pn = this.paramNames;
21538                 p[pn["sort"]] = this.sortInfo.field;
21539                 p[pn["dir"]] = this.sortInfo.direction;
21540             }
21541             if (this.multiSort) {
21542                 var pn = this.paramNames;
21543                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21544             }
21545             
21546             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21547         }
21548     },
21549
21550     /**
21551      * Reloads the Record cache from the configured Proxy using the configured Reader and
21552      * the options from the last load operation performed.
21553      * @param {Object} options (optional) An object containing properties which may override the options
21554      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21555      * the most recently used options are reused).
21556      */
21557     reload : function(options){
21558         this.load(Roo.applyIf(options||{}, this.lastOptions));
21559     },
21560
21561     // private
21562     // Called as a callback by the Reader during a load operation.
21563     loadRecords : function(o, options, success){
21564         if(!o || success === false){
21565             if(success !== false){
21566                 this.fireEvent("load", this, [], options, o);
21567             }
21568             if(options.callback){
21569                 options.callback.call(options.scope || this, [], options, false);
21570             }
21571             return;
21572         }
21573         // if data returned failure - throw an exception.
21574         if (o.success === false) {
21575             // show a message if no listener is registered.
21576             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21577                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21578             }
21579             // loadmask wil be hooked into this..
21580             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21581             return;
21582         }
21583         var r = o.records, t = o.totalRecords || r.length;
21584         
21585         this.fireEvent("beforeloadadd", this, r, options, o);
21586         
21587         if(!options || options.add !== true){
21588             if(this.pruneModifiedRecords){
21589                 this.modified = [];
21590             }
21591             for(var i = 0, len = r.length; i < len; i++){
21592                 r[i].join(this);
21593             }
21594             if(this.snapshot){
21595                 this.data = this.snapshot;
21596                 delete this.snapshot;
21597             }
21598             this.data.clear();
21599             this.data.addAll(r);
21600             this.totalLength = t;
21601             this.applySort();
21602             this.fireEvent("datachanged", this);
21603         }else{
21604             this.totalLength = Math.max(t, this.data.length+r.length);
21605             this.add(r);
21606         }
21607         this.fireEvent("load", this, r, options, o);
21608         if(options.callback){
21609             options.callback.call(options.scope || this, r, options, true);
21610         }
21611     },
21612
21613
21614     /**
21615      * Loads data from a passed data block. A Reader which understands the format of the data
21616      * must have been configured in the constructor.
21617      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21618      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21619      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21620      */
21621     loadData : function(o, append){
21622         var r = this.reader.readRecords(o);
21623         this.loadRecords(r, {add: append}, true);
21624     },
21625
21626     /**
21627      * Gets the number of cached records.
21628      * <p>
21629      * <em>If using paging, this may not be the total size of the dataset. If the data object
21630      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21631      * the data set size</em>
21632      */
21633     getCount : function(){
21634         return this.data.length || 0;
21635     },
21636
21637     /**
21638      * Gets the total number of records in the dataset as returned by the server.
21639      * <p>
21640      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21641      * the dataset size</em>
21642      */
21643     getTotalCount : function(){
21644         return this.totalLength || 0;
21645     },
21646
21647     /**
21648      * Returns the sort state of the Store as an object with two properties:
21649      * <pre><code>
21650  field {String} The name of the field by which the Records are sorted
21651  direction {String} The sort order, "ASC" or "DESC"
21652      * </code></pre>
21653      */
21654     getSortState : function(){
21655         return this.sortInfo;
21656     },
21657
21658     // private
21659     applySort : function(){
21660         if(this.sortInfo && !this.remoteSort){
21661             var s = this.sortInfo, f = s.field;
21662             var st = this.fields.get(f).sortType;
21663             var fn = function(r1, r2){
21664                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21665                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21666             };
21667             this.data.sort(s.direction, fn);
21668             if(this.snapshot && this.snapshot != this.data){
21669                 this.snapshot.sort(s.direction, fn);
21670             }
21671         }
21672     },
21673
21674     /**
21675      * Sets the default sort column and order to be used by the next load operation.
21676      * @param {String} fieldName The name of the field to sort by.
21677      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21678      */
21679     setDefaultSort : function(field, dir){
21680         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21681     },
21682
21683     /**
21684      * Sort the Records.
21685      * If remote sorting is used, the sort is performed on the server, and the cache is
21686      * reloaded. If local sorting is used, the cache is sorted internally.
21687      * @param {String} fieldName The name of the field to sort by.
21688      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21689      */
21690     sort : function(fieldName, dir){
21691         var f = this.fields.get(fieldName);
21692         if(!dir){
21693             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21694             
21695             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21696                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21697             }else{
21698                 dir = f.sortDir;
21699             }
21700         }
21701         this.sortToggle[f.name] = dir;
21702         this.sortInfo = {field: f.name, direction: dir};
21703         if(!this.remoteSort){
21704             this.applySort();
21705             this.fireEvent("datachanged", this);
21706         }else{
21707             this.load(this.lastOptions);
21708         }
21709     },
21710
21711     /**
21712      * Calls the specified function for each of the Records in the cache.
21713      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21714      * Returning <em>false</em> aborts and exits the iteration.
21715      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21716      */
21717     each : function(fn, scope){
21718         this.data.each(fn, scope);
21719     },
21720
21721     /**
21722      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21723      * (e.g., during paging).
21724      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21725      */
21726     getModifiedRecords : function(){
21727         return this.modified;
21728     },
21729
21730     // private
21731     createFilterFn : function(property, value, anyMatch){
21732         if(!value.exec){ // not a regex
21733             value = String(value);
21734             if(value.length == 0){
21735                 return false;
21736             }
21737             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21738         }
21739         return function(r){
21740             return value.test(r.data[property]);
21741         };
21742     },
21743
21744     /**
21745      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21746      * @param {String} property A field on your records
21747      * @param {Number} start The record index to start at (defaults to 0)
21748      * @param {Number} end The last record index to include (defaults to length - 1)
21749      * @return {Number} The sum
21750      */
21751     sum : function(property, start, end){
21752         var rs = this.data.items, v = 0;
21753         start = start || 0;
21754         end = (end || end === 0) ? end : rs.length-1;
21755
21756         for(var i = start; i <= end; i++){
21757             v += (rs[i].data[property] || 0);
21758         }
21759         return v;
21760     },
21761
21762     /**
21763      * Filter the records by a specified property.
21764      * @param {String} field A field on your records
21765      * @param {String/RegExp} value Either a string that the field
21766      * should start with or a RegExp to test against the field
21767      * @param {Boolean} anyMatch True to match any part not just the beginning
21768      */
21769     filter : function(property, value, anyMatch){
21770         var fn = this.createFilterFn(property, value, anyMatch);
21771         return fn ? this.filterBy(fn) : this.clearFilter();
21772     },
21773
21774     /**
21775      * Filter by a function. The specified function will be called with each
21776      * record in this data source. If the function returns true the record is included,
21777      * otherwise it is filtered.
21778      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21779      * @param {Object} scope (optional) The scope of the function (defaults to this)
21780      */
21781     filterBy : function(fn, scope){
21782         this.snapshot = this.snapshot || this.data;
21783         this.data = this.queryBy(fn, scope||this);
21784         this.fireEvent("datachanged", this);
21785     },
21786
21787     /**
21788      * Query the records by a specified property.
21789      * @param {String} field A field on your records
21790      * @param {String/RegExp} value Either a string that the field
21791      * should start with or a RegExp to test against the field
21792      * @param {Boolean} anyMatch True to match any part not just the beginning
21793      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21794      */
21795     query : function(property, value, anyMatch){
21796         var fn = this.createFilterFn(property, value, anyMatch);
21797         return fn ? this.queryBy(fn) : this.data.clone();
21798     },
21799
21800     /**
21801      * Query by a function. The specified function will be called with each
21802      * record in this data source. If the function returns true the record is included
21803      * in the results.
21804      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21805      * @param {Object} scope (optional) The scope of the function (defaults to this)
21806       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21807      **/
21808     queryBy : function(fn, scope){
21809         var data = this.snapshot || this.data;
21810         return data.filterBy(fn, scope||this);
21811     },
21812
21813     /**
21814      * Collects unique values for a particular dataIndex from this store.
21815      * @param {String} dataIndex The property to collect
21816      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21817      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21818      * @return {Array} An array of the unique values
21819      **/
21820     collect : function(dataIndex, allowNull, bypassFilter){
21821         var d = (bypassFilter === true && this.snapshot) ?
21822                 this.snapshot.items : this.data.items;
21823         var v, sv, r = [], l = {};
21824         for(var i = 0, len = d.length; i < len; i++){
21825             v = d[i].data[dataIndex];
21826             sv = String(v);
21827             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21828                 l[sv] = true;
21829                 r[r.length] = v;
21830             }
21831         }
21832         return r;
21833     },
21834
21835     /**
21836      * Revert to a view of the Record cache with no filtering applied.
21837      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21838      */
21839     clearFilter : function(suppressEvent){
21840         if(this.snapshot && this.snapshot != this.data){
21841             this.data = this.snapshot;
21842             delete this.snapshot;
21843             if(suppressEvent !== true){
21844                 this.fireEvent("datachanged", this);
21845             }
21846         }
21847     },
21848
21849     // private
21850     afterEdit : function(record){
21851         if(this.modified.indexOf(record) == -1){
21852             this.modified.push(record);
21853         }
21854         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21855     },
21856     
21857     // private
21858     afterReject : function(record){
21859         this.modified.remove(record);
21860         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21861     },
21862
21863     // private
21864     afterCommit : function(record){
21865         this.modified.remove(record);
21866         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21867     },
21868
21869     /**
21870      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21871      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21872      */
21873     commitChanges : function(){
21874         var m = this.modified.slice(0);
21875         this.modified = [];
21876         for(var i = 0, len = m.length; i < len; i++){
21877             m[i].commit();
21878         }
21879     },
21880
21881     /**
21882      * Cancel outstanding changes on all changed records.
21883      */
21884     rejectChanges : function(){
21885         var m = this.modified.slice(0);
21886         this.modified = [];
21887         for(var i = 0, len = m.length; i < len; i++){
21888             m[i].reject();
21889         }
21890     },
21891
21892     onMetaChange : function(meta, rtype, o){
21893         this.recordType = rtype;
21894         this.fields = rtype.prototype.fields;
21895         delete this.snapshot;
21896         this.sortInfo = meta.sortInfo || this.sortInfo;
21897         this.modified = [];
21898         this.fireEvent('metachange', this, this.reader.meta);
21899     },
21900     
21901     moveIndex : function(data, type)
21902     {
21903         var index = this.indexOf(data);
21904         
21905         var newIndex = index + type;
21906         
21907         this.remove(data);
21908         
21909         this.insert(newIndex, data);
21910         
21911     }
21912 });/*
21913  * Based on:
21914  * Ext JS Library 1.1.1
21915  * Copyright(c) 2006-2007, Ext JS, LLC.
21916  *
21917  * Originally Released Under LGPL - original licence link has changed is not relivant.
21918  *
21919  * Fork - LGPL
21920  * <script type="text/javascript">
21921  */
21922
21923 /**
21924  * @class Roo.data.SimpleStore
21925  * @extends Roo.data.Store
21926  * Small helper class to make creating Stores from Array data easier.
21927  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21928  * @cfg {Array} fields An array of field definition objects, or field name strings.
21929  * @cfg {Array} data The multi-dimensional array of data
21930  * @constructor
21931  * @param {Object} config
21932  */
21933 Roo.data.SimpleStore = function(config){
21934     Roo.data.SimpleStore.superclass.constructor.call(this, {
21935         isLocal : true,
21936         reader: new Roo.data.ArrayReader({
21937                 id: config.id
21938             },
21939             Roo.data.Record.create(config.fields)
21940         ),
21941         proxy : new Roo.data.MemoryProxy(config.data)
21942     });
21943     this.load();
21944 };
21945 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21946  * Based on:
21947  * Ext JS Library 1.1.1
21948  * Copyright(c) 2006-2007, Ext JS, LLC.
21949  *
21950  * Originally Released Under LGPL - original licence link has changed is not relivant.
21951  *
21952  * Fork - LGPL
21953  * <script type="text/javascript">
21954  */
21955
21956 /**
21957 /**
21958  * @extends Roo.data.Store
21959  * @class Roo.data.JsonStore
21960  * Small helper class to make creating Stores for JSON data easier. <br/>
21961 <pre><code>
21962 var store = new Roo.data.JsonStore({
21963     url: 'get-images.php',
21964     root: 'images',
21965     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21966 });
21967 </code></pre>
21968  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21969  * JsonReader and HttpProxy (unless inline data is provided).</b>
21970  * @cfg {Array} fields An array of field definition objects, or field name strings.
21971  * @constructor
21972  * @param {Object} config
21973  */
21974 Roo.data.JsonStore = function(c){
21975     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21976         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21977         reader: new Roo.data.JsonReader(c, c.fields)
21978     }));
21979 };
21980 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21981  * Based on:
21982  * Ext JS Library 1.1.1
21983  * Copyright(c) 2006-2007, Ext JS, LLC.
21984  *
21985  * Originally Released Under LGPL - original licence link has changed is not relivant.
21986  *
21987  * Fork - LGPL
21988  * <script type="text/javascript">
21989  */
21990
21991  
21992 Roo.data.Field = function(config){
21993     if(typeof config == "string"){
21994         config = {name: config};
21995     }
21996     Roo.apply(this, config);
21997     
21998     if(!this.type){
21999         this.type = "auto";
22000     }
22001     
22002     var st = Roo.data.SortTypes;
22003     // named sortTypes are supported, here we look them up
22004     if(typeof this.sortType == "string"){
22005         this.sortType = st[this.sortType];
22006     }
22007     
22008     // set default sortType for strings and dates
22009     if(!this.sortType){
22010         switch(this.type){
22011             case "string":
22012                 this.sortType = st.asUCString;
22013                 break;
22014             case "date":
22015                 this.sortType = st.asDate;
22016                 break;
22017             default:
22018                 this.sortType = st.none;
22019         }
22020     }
22021
22022     // define once
22023     var stripRe = /[\$,%]/g;
22024
22025     // prebuilt conversion function for this field, instead of
22026     // switching every time we're reading a value
22027     if(!this.convert){
22028         var cv, dateFormat = this.dateFormat;
22029         switch(this.type){
22030             case "":
22031             case "auto":
22032             case undefined:
22033                 cv = function(v){ return v; };
22034                 break;
22035             case "string":
22036                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22037                 break;
22038             case "int":
22039                 cv = function(v){
22040                     return v !== undefined && v !== null && v !== '' ?
22041                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22042                     };
22043                 break;
22044             case "float":
22045                 cv = function(v){
22046                     return v !== undefined && v !== null && v !== '' ?
22047                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22048                     };
22049                 break;
22050             case "bool":
22051             case "boolean":
22052                 cv = function(v){ return v === true || v === "true" || v == 1; };
22053                 break;
22054             case "date":
22055                 cv = function(v){
22056                     if(!v){
22057                         return '';
22058                     }
22059                     if(v instanceof Date){
22060                         return v;
22061                     }
22062                     if(dateFormat){
22063                         if(dateFormat == "timestamp"){
22064                             return new Date(v*1000);
22065                         }
22066                         return Date.parseDate(v, dateFormat);
22067                     }
22068                     var parsed = Date.parse(v);
22069                     return parsed ? new Date(parsed) : null;
22070                 };
22071              break;
22072             
22073         }
22074         this.convert = cv;
22075     }
22076 };
22077
22078 Roo.data.Field.prototype = {
22079     dateFormat: null,
22080     defaultValue: "",
22081     mapping: null,
22082     sortType : null,
22083     sortDir : "ASC"
22084 };/*
22085  * Based on:
22086  * Ext JS Library 1.1.1
22087  * Copyright(c) 2006-2007, Ext JS, LLC.
22088  *
22089  * Originally Released Under LGPL - original licence link has changed is not relivant.
22090  *
22091  * Fork - LGPL
22092  * <script type="text/javascript">
22093  */
22094  
22095 // Base class for reading structured data from a data source.  This class is intended to be
22096 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22097
22098 /**
22099  * @class Roo.data.DataReader
22100  * Base class for reading structured data from a data source.  This class is intended to be
22101  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22102  */
22103
22104 Roo.data.DataReader = function(meta, recordType){
22105     
22106     this.meta = meta;
22107     
22108     this.recordType = recordType instanceof Array ? 
22109         Roo.data.Record.create(recordType) : recordType;
22110 };
22111
22112 Roo.data.DataReader.prototype = {
22113      /**
22114      * Create an empty record
22115      * @param {Object} data (optional) - overlay some values
22116      * @return {Roo.data.Record} record created.
22117      */
22118     newRow :  function(d) {
22119         var da =  {};
22120         this.recordType.prototype.fields.each(function(c) {
22121             switch( c.type) {
22122                 case 'int' : da[c.name] = 0; break;
22123                 case 'date' : da[c.name] = new Date(); break;
22124                 case 'float' : da[c.name] = 0.0; break;
22125                 case 'boolean' : da[c.name] = false; break;
22126                 default : da[c.name] = ""; break;
22127             }
22128             
22129         });
22130         return new this.recordType(Roo.apply(da, d));
22131     }
22132     
22133 };/*
22134  * Based on:
22135  * Ext JS Library 1.1.1
22136  * Copyright(c) 2006-2007, Ext JS, LLC.
22137  *
22138  * Originally Released Under LGPL - original licence link has changed is not relivant.
22139  *
22140  * Fork - LGPL
22141  * <script type="text/javascript">
22142  */
22143
22144 /**
22145  * @class Roo.data.DataProxy
22146  * @extends Roo.data.Observable
22147  * This class is an abstract base class for implementations which provide retrieval of
22148  * unformatted data objects.<br>
22149  * <p>
22150  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22151  * (of the appropriate type which knows how to parse the data object) to provide a block of
22152  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22153  * <p>
22154  * Custom implementations must implement the load method as described in
22155  * {@link Roo.data.HttpProxy#load}.
22156  */
22157 Roo.data.DataProxy = function(){
22158     this.addEvents({
22159         /**
22160          * @event beforeload
22161          * Fires before a network request is made to retrieve a data object.
22162          * @param {Object} This DataProxy object.
22163          * @param {Object} params The params parameter to the load function.
22164          */
22165         beforeload : true,
22166         /**
22167          * @event load
22168          * Fires before the load method's callback is called.
22169          * @param {Object} This DataProxy object.
22170          * @param {Object} o The data object.
22171          * @param {Object} arg The callback argument object passed to the load function.
22172          */
22173         load : true,
22174         /**
22175          * @event loadexception
22176          * Fires if an Exception occurs during data retrieval.
22177          * @param {Object} This DataProxy object.
22178          * @param {Object} o The data object.
22179          * @param {Object} arg The callback argument object passed to the load function.
22180          * @param {Object} e The Exception.
22181          */
22182         loadexception : true
22183     });
22184     Roo.data.DataProxy.superclass.constructor.call(this);
22185 };
22186
22187 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22188
22189     /**
22190      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22191      */
22192 /*
22193  * Based on:
22194  * Ext JS Library 1.1.1
22195  * Copyright(c) 2006-2007, Ext JS, LLC.
22196  *
22197  * Originally Released Under LGPL - original licence link has changed is not relivant.
22198  *
22199  * Fork - LGPL
22200  * <script type="text/javascript">
22201  */
22202 /**
22203  * @class Roo.data.MemoryProxy
22204  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22205  * to the Reader when its load method is called.
22206  * @constructor
22207  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22208  */
22209 Roo.data.MemoryProxy = function(data){
22210     if (data.data) {
22211         data = data.data;
22212     }
22213     Roo.data.MemoryProxy.superclass.constructor.call(this);
22214     this.data = data;
22215 };
22216
22217 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22218     /**
22219      * Load data from the requested source (in this case an in-memory
22220      * data object passed to the constructor), read the data object into
22221      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22222      * process that block using the passed callback.
22223      * @param {Object} params This parameter is not used by the MemoryProxy class.
22224      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22225      * object into a block of Roo.data.Records.
22226      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22227      * The function must be passed <ul>
22228      * <li>The Record block object</li>
22229      * <li>The "arg" argument from the load function</li>
22230      * <li>A boolean success indicator</li>
22231      * </ul>
22232      * @param {Object} scope The scope in which to call the callback
22233      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22234      */
22235     load : function(params, reader, callback, scope, arg){
22236         params = params || {};
22237         var result;
22238         try {
22239             result = reader.readRecords(this.data);
22240         }catch(e){
22241             this.fireEvent("loadexception", this, arg, null, e);
22242             callback.call(scope, null, arg, false);
22243             return;
22244         }
22245         callback.call(scope, result, arg, true);
22246     },
22247     
22248     // private
22249     update : function(params, records){
22250         
22251     }
22252 });/*
22253  * Based on:
22254  * Ext JS Library 1.1.1
22255  * Copyright(c) 2006-2007, Ext JS, LLC.
22256  *
22257  * Originally Released Under LGPL - original licence link has changed is not relivant.
22258  *
22259  * Fork - LGPL
22260  * <script type="text/javascript">
22261  */
22262 /**
22263  * @class Roo.data.HttpProxy
22264  * @extends Roo.data.DataProxy
22265  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22266  * configured to reference a certain URL.<br><br>
22267  * <p>
22268  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22269  * from which the running page was served.<br><br>
22270  * <p>
22271  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22272  * <p>
22273  * Be aware that to enable the browser to parse an XML document, the server must set
22274  * the Content-Type header in the HTTP response to "text/xml".
22275  * @constructor
22276  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22277  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22278  * will be used to make the request.
22279  */
22280 Roo.data.HttpProxy = function(conn){
22281     Roo.data.HttpProxy.superclass.constructor.call(this);
22282     // is conn a conn config or a real conn?
22283     this.conn = conn;
22284     this.useAjax = !conn || !conn.events;
22285   
22286 };
22287
22288 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22289     // thse are take from connection...
22290     
22291     /**
22292      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22293      */
22294     /**
22295      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22296      * extra parameters to each request made by this object. (defaults to undefined)
22297      */
22298     /**
22299      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22300      *  to each request made by this object. (defaults to undefined)
22301      */
22302     /**
22303      * @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)
22304      */
22305     /**
22306      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22307      */
22308      /**
22309      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22310      * @type Boolean
22311      */
22312   
22313
22314     /**
22315      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22316      * @type Boolean
22317      */
22318     /**
22319      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22320      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22321      * a finer-grained basis than the DataProxy events.
22322      */
22323     getConnection : function(){
22324         return this.useAjax ? Roo.Ajax : this.conn;
22325     },
22326
22327     /**
22328      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22329      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22330      * process that block using the passed callback.
22331      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22332      * for the request to the remote server.
22333      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22334      * object into a block of Roo.data.Records.
22335      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22336      * The function must be passed <ul>
22337      * <li>The Record block object</li>
22338      * <li>The "arg" argument from the load function</li>
22339      * <li>A boolean success indicator</li>
22340      * </ul>
22341      * @param {Object} scope The scope in which to call the callback
22342      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22343      */
22344     load : function(params, reader, callback, scope, arg){
22345         if(this.fireEvent("beforeload", this, params) !== false){
22346             var  o = {
22347                 params : params || {},
22348                 request: {
22349                     callback : callback,
22350                     scope : scope,
22351                     arg : arg
22352                 },
22353                 reader: reader,
22354                 callback : this.loadResponse,
22355                 scope: this
22356             };
22357             if(this.useAjax){
22358                 Roo.applyIf(o, this.conn);
22359                 if(this.activeRequest){
22360                     Roo.Ajax.abort(this.activeRequest);
22361                 }
22362                 this.activeRequest = Roo.Ajax.request(o);
22363             }else{
22364                 this.conn.request(o);
22365             }
22366         }else{
22367             callback.call(scope||this, null, arg, false);
22368         }
22369     },
22370
22371     // private
22372     loadResponse : function(o, success, response){
22373         delete this.activeRequest;
22374         if(!success){
22375             this.fireEvent("loadexception", this, o, response);
22376             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22377             return;
22378         }
22379         var result;
22380         try {
22381             result = o.reader.read(response);
22382         }catch(e){
22383             this.fireEvent("loadexception", this, o, response, e);
22384             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22385             return;
22386         }
22387         
22388         this.fireEvent("load", this, o, o.request.arg);
22389         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22390     },
22391
22392     // private
22393     update : function(dataSet){
22394
22395     },
22396
22397     // private
22398     updateResponse : function(dataSet){
22399
22400     }
22401 });/*
22402  * Based on:
22403  * Ext JS Library 1.1.1
22404  * Copyright(c) 2006-2007, Ext JS, LLC.
22405  *
22406  * Originally Released Under LGPL - original licence link has changed is not relivant.
22407  *
22408  * Fork - LGPL
22409  * <script type="text/javascript">
22410  */
22411
22412 /**
22413  * @class Roo.data.ScriptTagProxy
22414  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22415  * other than the originating domain of the running page.<br><br>
22416  * <p>
22417  * <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
22418  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22419  * <p>
22420  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22421  * source code that is used as the source inside a &lt;script> tag.<br><br>
22422  * <p>
22423  * In order for the browser to process the returned data, the server must wrap the data object
22424  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22425  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22426  * depending on whether the callback name was passed:
22427  * <p>
22428  * <pre><code>
22429 boolean scriptTag = false;
22430 String cb = request.getParameter("callback");
22431 if (cb != null) {
22432     scriptTag = true;
22433     response.setContentType("text/javascript");
22434 } else {
22435     response.setContentType("application/x-json");
22436 }
22437 Writer out = response.getWriter();
22438 if (scriptTag) {
22439     out.write(cb + "(");
22440 }
22441 out.print(dataBlock.toJsonString());
22442 if (scriptTag) {
22443     out.write(");");
22444 }
22445 </pre></code>
22446  *
22447  * @constructor
22448  * @param {Object} config A configuration object.
22449  */
22450 Roo.data.ScriptTagProxy = function(config){
22451     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22452     Roo.apply(this, config);
22453     this.head = document.getElementsByTagName("head")[0];
22454 };
22455
22456 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22457
22458 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22459     /**
22460      * @cfg {String} url The URL from which to request the data object.
22461      */
22462     /**
22463      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22464      */
22465     timeout : 30000,
22466     /**
22467      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22468      * the server the name of the callback function set up by the load call to process the returned data object.
22469      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22470      * javascript output which calls this named function passing the data object as its only parameter.
22471      */
22472     callbackParam : "callback",
22473     /**
22474      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22475      * name to the request.
22476      */
22477     nocache : true,
22478
22479     /**
22480      * Load data from the configured URL, read the data object into
22481      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22482      * process that block using the passed callback.
22483      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22484      * for the request to the remote server.
22485      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22486      * object into a block of Roo.data.Records.
22487      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22488      * The function must be passed <ul>
22489      * <li>The Record block object</li>
22490      * <li>The "arg" argument from the load function</li>
22491      * <li>A boolean success indicator</li>
22492      * </ul>
22493      * @param {Object} scope The scope in which to call the callback
22494      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22495      */
22496     load : function(params, reader, callback, scope, arg){
22497         if(this.fireEvent("beforeload", this, params) !== false){
22498
22499             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22500
22501             var url = this.url;
22502             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22503             if(this.nocache){
22504                 url += "&_dc=" + (new Date().getTime());
22505             }
22506             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22507             var trans = {
22508                 id : transId,
22509                 cb : "stcCallback"+transId,
22510                 scriptId : "stcScript"+transId,
22511                 params : params,
22512                 arg : arg,
22513                 url : url,
22514                 callback : callback,
22515                 scope : scope,
22516                 reader : reader
22517             };
22518             var conn = this;
22519
22520             window[trans.cb] = function(o){
22521                 conn.handleResponse(o, trans);
22522             };
22523
22524             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22525
22526             if(this.autoAbort !== false){
22527                 this.abort();
22528             }
22529
22530             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22531
22532             var script = document.createElement("script");
22533             script.setAttribute("src", url);
22534             script.setAttribute("type", "text/javascript");
22535             script.setAttribute("id", trans.scriptId);
22536             this.head.appendChild(script);
22537
22538             this.trans = trans;
22539         }else{
22540             callback.call(scope||this, null, arg, false);
22541         }
22542     },
22543
22544     // private
22545     isLoading : function(){
22546         return this.trans ? true : false;
22547     },
22548
22549     /**
22550      * Abort the current server request.
22551      */
22552     abort : function(){
22553         if(this.isLoading()){
22554             this.destroyTrans(this.trans);
22555         }
22556     },
22557
22558     // private
22559     destroyTrans : function(trans, isLoaded){
22560         this.head.removeChild(document.getElementById(trans.scriptId));
22561         clearTimeout(trans.timeoutId);
22562         if(isLoaded){
22563             window[trans.cb] = undefined;
22564             try{
22565                 delete window[trans.cb];
22566             }catch(e){}
22567         }else{
22568             // if hasn't been loaded, wait for load to remove it to prevent script error
22569             window[trans.cb] = function(){
22570                 window[trans.cb] = undefined;
22571                 try{
22572                     delete window[trans.cb];
22573                 }catch(e){}
22574             };
22575         }
22576     },
22577
22578     // private
22579     handleResponse : function(o, trans){
22580         this.trans = false;
22581         this.destroyTrans(trans, true);
22582         var result;
22583         try {
22584             result = trans.reader.readRecords(o);
22585         }catch(e){
22586             this.fireEvent("loadexception", this, o, trans.arg, e);
22587             trans.callback.call(trans.scope||window, null, trans.arg, false);
22588             return;
22589         }
22590         this.fireEvent("load", this, o, trans.arg);
22591         trans.callback.call(trans.scope||window, result, trans.arg, true);
22592     },
22593
22594     // private
22595     handleFailure : function(trans){
22596         this.trans = false;
22597         this.destroyTrans(trans, false);
22598         this.fireEvent("loadexception", this, null, trans.arg);
22599         trans.callback.call(trans.scope||window, null, trans.arg, false);
22600     }
22601 });/*
22602  * Based on:
22603  * Ext JS Library 1.1.1
22604  * Copyright(c) 2006-2007, Ext JS, LLC.
22605  *
22606  * Originally Released Under LGPL - original licence link has changed is not relivant.
22607  *
22608  * Fork - LGPL
22609  * <script type="text/javascript">
22610  */
22611
22612 /**
22613  * @class Roo.data.JsonReader
22614  * @extends Roo.data.DataReader
22615  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22616  * based on mappings in a provided Roo.data.Record constructor.
22617  * 
22618  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22619  * in the reply previously. 
22620  * 
22621  * <p>
22622  * Example code:
22623  * <pre><code>
22624 var RecordDef = Roo.data.Record.create([
22625     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22626     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22627 ]);
22628 var myReader = new Roo.data.JsonReader({
22629     totalProperty: "results",    // The property which contains the total dataset size (optional)
22630     root: "rows",                // The property which contains an Array of row objects
22631     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22632 }, RecordDef);
22633 </code></pre>
22634  * <p>
22635  * This would consume a JSON file like this:
22636  * <pre><code>
22637 { 'results': 2, 'rows': [
22638     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22639     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22640 }
22641 </code></pre>
22642  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22643  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22644  * paged from the remote server.
22645  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22646  * @cfg {String} root name of the property which contains the Array of row objects.
22647  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22648  * @cfg {Array} fields Array of field definition objects
22649  * @constructor
22650  * Create a new JsonReader
22651  * @param {Object} meta Metadata configuration options
22652  * @param {Object} recordType Either an Array of field definition objects,
22653  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22654  */
22655 Roo.data.JsonReader = function(meta, recordType){
22656     
22657     meta = meta || {};
22658     // set some defaults:
22659     Roo.applyIf(meta, {
22660         totalProperty: 'total',
22661         successProperty : 'success',
22662         root : 'data',
22663         id : 'id'
22664     });
22665     
22666     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22667 };
22668 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22669     
22670     /**
22671      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22672      * Used by Store query builder to append _requestMeta to params.
22673      * 
22674      */
22675     metaFromRemote : false,
22676     /**
22677      * This method is only used by a DataProxy which has retrieved data from a remote server.
22678      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22679      * @return {Object} data A data block which is used by an Roo.data.Store object as
22680      * a cache of Roo.data.Records.
22681      */
22682     read : function(response){
22683         var json = response.responseText;
22684        
22685         var o = /* eval:var:o */ eval("("+json+")");
22686         if(!o) {
22687             throw {message: "JsonReader.read: Json object not found"};
22688         }
22689         
22690         if(o.metaData){
22691             
22692             delete this.ef;
22693             this.metaFromRemote = true;
22694             this.meta = o.metaData;
22695             this.recordType = Roo.data.Record.create(o.metaData.fields);
22696             this.onMetaChange(this.meta, this.recordType, o);
22697         }
22698         return this.readRecords(o);
22699     },
22700
22701     // private function a store will implement
22702     onMetaChange : function(meta, recordType, o){
22703
22704     },
22705
22706     /**
22707          * @ignore
22708          */
22709     simpleAccess: function(obj, subsc) {
22710         return obj[subsc];
22711     },
22712
22713         /**
22714          * @ignore
22715          */
22716     getJsonAccessor: function(){
22717         var re = /[\[\.]/;
22718         return function(expr) {
22719             try {
22720                 return(re.test(expr))
22721                     ? new Function("obj", "return obj." + expr)
22722                     : function(obj){
22723                         return obj[expr];
22724                     };
22725             } catch(e){}
22726             return Roo.emptyFn;
22727         };
22728     }(),
22729
22730     /**
22731      * Create a data block containing Roo.data.Records from an XML document.
22732      * @param {Object} o An object which contains an Array of row objects in the property specified
22733      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22734      * which contains the total size of the dataset.
22735      * @return {Object} data A data block which is used by an Roo.data.Store object as
22736      * a cache of Roo.data.Records.
22737      */
22738     readRecords : function(o){
22739         /**
22740          * After any data loads, the raw JSON data is available for further custom processing.
22741          * @type Object
22742          */
22743         this.o = o;
22744         var s = this.meta, Record = this.recordType,
22745             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22746
22747 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22748         if (!this.ef) {
22749             if(s.totalProperty) {
22750                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22751                 }
22752                 if(s.successProperty) {
22753                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22754                 }
22755                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22756                 if (s.id) {
22757                         var g = this.getJsonAccessor(s.id);
22758                         this.getId = function(rec) {
22759                                 var r = g(rec);  
22760                                 return (r === undefined || r === "") ? null : r;
22761                         };
22762                 } else {
22763                         this.getId = function(){return null;};
22764                 }
22765             this.ef = [];
22766             for(var jj = 0; jj < fl; jj++){
22767                 f = fi[jj];
22768                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22769                 this.ef[jj] = this.getJsonAccessor(map);
22770             }
22771         }
22772
22773         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22774         if(s.totalProperty){
22775             var vt = parseInt(this.getTotal(o), 10);
22776             if(!isNaN(vt)){
22777                 totalRecords = vt;
22778             }
22779         }
22780         if(s.successProperty){
22781             var vs = this.getSuccess(o);
22782             if(vs === false || vs === 'false'){
22783                 success = false;
22784             }
22785         }
22786         var records = [];
22787         for(var i = 0; i < c; i++){
22788                 var n = root[i];
22789             var values = {};
22790             var id = this.getId(n);
22791             for(var j = 0; j < fl; j++){
22792                 f = fi[j];
22793             var v = this.ef[j](n);
22794             if (!f.convert) {
22795                 Roo.log('missing convert for ' + f.name);
22796                 Roo.log(f);
22797                 continue;
22798             }
22799             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22800             }
22801             var record = new Record(values, id);
22802             record.json = n;
22803             records[i] = record;
22804         }
22805         return {
22806             raw : o,
22807             success : success,
22808             records : records,
22809             totalRecords : totalRecords
22810         };
22811     }
22812 });/*
22813  * Based on:
22814  * Ext JS Library 1.1.1
22815  * Copyright(c) 2006-2007, Ext JS, LLC.
22816  *
22817  * Originally Released Under LGPL - original licence link has changed is not relivant.
22818  *
22819  * Fork - LGPL
22820  * <script type="text/javascript">
22821  */
22822
22823 /**
22824  * @class Roo.data.XmlReader
22825  * @extends Roo.data.DataReader
22826  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22827  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22828  * <p>
22829  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22830  * header in the HTTP response must be set to "text/xml".</em>
22831  * <p>
22832  * Example code:
22833  * <pre><code>
22834 var RecordDef = Roo.data.Record.create([
22835    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22836    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22837 ]);
22838 var myReader = new Roo.data.XmlReader({
22839    totalRecords: "results", // The element which contains the total dataset size (optional)
22840    record: "row",           // The repeated element which contains row information
22841    id: "id"                 // The element within the row that provides an ID for the record (optional)
22842 }, RecordDef);
22843 </code></pre>
22844  * <p>
22845  * This would consume an XML file like this:
22846  * <pre><code>
22847 &lt;?xml?>
22848 &lt;dataset>
22849  &lt;results>2&lt;/results>
22850  &lt;row>
22851    &lt;id>1&lt;/id>
22852    &lt;name>Bill&lt;/name>
22853    &lt;occupation>Gardener&lt;/occupation>
22854  &lt;/row>
22855  &lt;row>
22856    &lt;id>2&lt;/id>
22857    &lt;name>Ben&lt;/name>
22858    &lt;occupation>Horticulturalist&lt;/occupation>
22859  &lt;/row>
22860 &lt;/dataset>
22861 </code></pre>
22862  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22863  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22864  * paged from the remote server.
22865  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22866  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22867  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22868  * a record identifier value.
22869  * @constructor
22870  * Create a new XmlReader
22871  * @param {Object} meta Metadata configuration options
22872  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22873  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22874  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22875  */
22876 Roo.data.XmlReader = function(meta, recordType){
22877     meta = meta || {};
22878     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22879 };
22880 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22881     /**
22882      * This method is only used by a DataProxy which has retrieved data from a remote server.
22883          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22884          * to contain a method called 'responseXML' that returns an XML document object.
22885      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22886      * a cache of Roo.data.Records.
22887      */
22888     read : function(response){
22889         var doc = response.responseXML;
22890         if(!doc) {
22891             throw {message: "XmlReader.read: XML Document not available"};
22892         }
22893         return this.readRecords(doc);
22894     },
22895
22896     /**
22897      * Create a data block containing Roo.data.Records from an XML document.
22898          * @param {Object} doc A parsed XML document.
22899      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22900      * a cache of Roo.data.Records.
22901      */
22902     readRecords : function(doc){
22903         /**
22904          * After any data loads/reads, the raw XML Document is available for further custom processing.
22905          * @type XMLDocument
22906          */
22907         this.xmlData = doc;
22908         var root = doc.documentElement || doc;
22909         var q = Roo.DomQuery;
22910         var recordType = this.recordType, fields = recordType.prototype.fields;
22911         var sid = this.meta.id;
22912         var totalRecords = 0, success = true;
22913         if(this.meta.totalRecords){
22914             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22915         }
22916         
22917         if(this.meta.success){
22918             var sv = q.selectValue(this.meta.success, root, true);
22919             success = sv !== false && sv !== 'false';
22920         }
22921         var records = [];
22922         var ns = q.select(this.meta.record, root);
22923         for(var i = 0, len = ns.length; i < len; i++) {
22924                 var n = ns[i];
22925                 var values = {};
22926                 var id = sid ? q.selectValue(sid, n) : undefined;
22927                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22928                     var f = fields.items[j];
22929                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22930                     v = f.convert(v);
22931                     values[f.name] = v;
22932                 }
22933                 var record = new recordType(values, id);
22934                 record.node = n;
22935                 records[records.length] = record;
22936             }
22937
22938             return {
22939                 success : success,
22940                 records : records,
22941                 totalRecords : totalRecords || records.length
22942             };
22943     }
22944 });/*
22945  * Based on:
22946  * Ext JS Library 1.1.1
22947  * Copyright(c) 2006-2007, Ext JS, LLC.
22948  *
22949  * Originally Released Under LGPL - original licence link has changed is not relivant.
22950  *
22951  * Fork - LGPL
22952  * <script type="text/javascript">
22953  */
22954
22955 /**
22956  * @class Roo.data.ArrayReader
22957  * @extends Roo.data.DataReader
22958  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22959  * Each element of that Array represents a row of data fields. The
22960  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22961  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22962  * <p>
22963  * Example code:.
22964  * <pre><code>
22965 var RecordDef = Roo.data.Record.create([
22966     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22967     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22968 ]);
22969 var myReader = new Roo.data.ArrayReader({
22970     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22971 }, RecordDef);
22972 </code></pre>
22973  * <p>
22974  * This would consume an Array like this:
22975  * <pre><code>
22976 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22977   </code></pre>
22978  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22979  * @constructor
22980  * Create a new JsonReader
22981  * @param {Object} meta Metadata configuration options.
22982  * @param {Object} recordType Either an Array of field definition objects
22983  * as specified to {@link Roo.data.Record#create},
22984  * or an {@link Roo.data.Record} object
22985  * created using {@link Roo.data.Record#create}.
22986  */
22987 Roo.data.ArrayReader = function(meta, recordType){
22988     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22989 };
22990
22991 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22992     /**
22993      * Create a data block containing Roo.data.Records from an XML document.
22994      * @param {Object} o An Array of row objects which represents the dataset.
22995      * @return {Object} data A data block which is used by an Roo.data.Store object as
22996      * a cache of Roo.data.Records.
22997      */
22998     readRecords : function(o){
22999         var sid = this.meta ? this.meta.id : null;
23000         var recordType = this.recordType, fields = recordType.prototype.fields;
23001         var records = [];
23002         var root = o;
23003             for(var i = 0; i < root.length; i++){
23004                     var n = root[i];
23005                 var values = {};
23006                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
23007                 for(var j = 0, jlen = fields.length; j < jlen; j++){
23008                 var f = fields.items[j];
23009                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
23010                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
23011                 v = f.convert(v);
23012                 values[f.name] = v;
23013             }
23014                 var record = new recordType(values, id);
23015                 record.json = n;
23016                 records[records.length] = record;
23017             }
23018             return {
23019                 records : records,
23020                 totalRecords : records.length
23021             };
23022     }
23023 });/*
23024  * Based on:
23025  * Ext JS Library 1.1.1
23026  * Copyright(c) 2006-2007, Ext JS, LLC.
23027  *
23028  * Originally Released Under LGPL - original licence link has changed is not relivant.
23029  *
23030  * Fork - LGPL
23031  * <script type="text/javascript">
23032  */
23033
23034
23035 /**
23036  * @class Roo.data.Tree
23037  * @extends Roo.util.Observable
23038  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23039  * in the tree have most standard DOM functionality.
23040  * @constructor
23041  * @param {Node} root (optional) The root node
23042  */
23043 Roo.data.Tree = function(root){
23044    this.nodeHash = {};
23045    /**
23046     * The root node for this tree
23047     * @type Node
23048     */
23049    this.root = null;
23050    if(root){
23051        this.setRootNode(root);
23052    }
23053    this.addEvents({
23054        /**
23055         * @event append
23056         * Fires when a new child node is appended to a node in this tree.
23057         * @param {Tree} tree The owner tree
23058         * @param {Node} parent The parent node
23059         * @param {Node} node The newly appended node
23060         * @param {Number} index The index of the newly appended node
23061         */
23062        "append" : true,
23063        /**
23064         * @event remove
23065         * Fires when a child node is removed from a node in this tree.
23066         * @param {Tree} tree The owner tree
23067         * @param {Node} parent The parent node
23068         * @param {Node} node The child node removed
23069         */
23070        "remove" : true,
23071        /**
23072         * @event move
23073         * Fires when a node is moved to a new location in the tree
23074         * @param {Tree} tree The owner tree
23075         * @param {Node} node The node moved
23076         * @param {Node} oldParent The old parent of this node
23077         * @param {Node} newParent The new parent of this node
23078         * @param {Number} index The index it was moved to
23079         */
23080        "move" : true,
23081        /**
23082         * @event insert
23083         * Fires when a new child node is inserted in a node in this tree.
23084         * @param {Tree} tree The owner tree
23085         * @param {Node} parent The parent node
23086         * @param {Node} node The child node inserted
23087         * @param {Node} refNode The child node the node was inserted before
23088         */
23089        "insert" : true,
23090        /**
23091         * @event beforeappend
23092         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23093         * @param {Tree} tree The owner tree
23094         * @param {Node} parent The parent node
23095         * @param {Node} node The child node to be appended
23096         */
23097        "beforeappend" : true,
23098        /**
23099         * @event beforeremove
23100         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23101         * @param {Tree} tree The owner tree
23102         * @param {Node} parent The parent node
23103         * @param {Node} node The child node to be removed
23104         */
23105        "beforeremove" : true,
23106        /**
23107         * @event beforemove
23108         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23109         * @param {Tree} tree The owner tree
23110         * @param {Node} node The node being moved
23111         * @param {Node} oldParent The parent of the node
23112         * @param {Node} newParent The new parent the node is moving to
23113         * @param {Number} index The index it is being moved to
23114         */
23115        "beforemove" : true,
23116        /**
23117         * @event beforeinsert
23118         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23119         * @param {Tree} tree The owner tree
23120         * @param {Node} parent The parent node
23121         * @param {Node} node The child node to be inserted
23122         * @param {Node} refNode The child node the node is being inserted before
23123         */
23124        "beforeinsert" : true
23125    });
23126
23127     Roo.data.Tree.superclass.constructor.call(this);
23128 };
23129
23130 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23131     pathSeparator: "/",
23132
23133     proxyNodeEvent : function(){
23134         return this.fireEvent.apply(this, arguments);
23135     },
23136
23137     /**
23138      * Returns the root node for this tree.
23139      * @return {Node}
23140      */
23141     getRootNode : function(){
23142         return this.root;
23143     },
23144
23145     /**
23146      * Sets the root node for this tree.
23147      * @param {Node} node
23148      * @return {Node}
23149      */
23150     setRootNode : function(node){
23151         this.root = node;
23152         node.ownerTree = this;
23153         node.isRoot = true;
23154         this.registerNode(node);
23155         return node;
23156     },
23157
23158     /**
23159      * Gets a node in this tree by its id.
23160      * @param {String} id
23161      * @return {Node}
23162      */
23163     getNodeById : function(id){
23164         return this.nodeHash[id];
23165     },
23166
23167     registerNode : function(node){
23168         this.nodeHash[node.id] = node;
23169     },
23170
23171     unregisterNode : function(node){
23172         delete this.nodeHash[node.id];
23173     },
23174
23175     toString : function(){
23176         return "[Tree"+(this.id?" "+this.id:"")+"]";
23177     }
23178 });
23179
23180 /**
23181  * @class Roo.data.Node
23182  * @extends Roo.util.Observable
23183  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23184  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23185  * @constructor
23186  * @param {Object} attributes The attributes/config for the node
23187  */
23188 Roo.data.Node = function(attributes){
23189     /**
23190      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23191      * @type {Object}
23192      */
23193     this.attributes = attributes || {};
23194     this.leaf = this.attributes.leaf;
23195     /**
23196      * The node id. @type String
23197      */
23198     this.id = this.attributes.id;
23199     if(!this.id){
23200         this.id = Roo.id(null, "ynode-");
23201         this.attributes.id = this.id;
23202     }
23203      
23204     
23205     /**
23206      * All child nodes of this node. @type Array
23207      */
23208     this.childNodes = [];
23209     if(!this.childNodes.indexOf){ // indexOf is a must
23210         this.childNodes.indexOf = function(o){
23211             for(var i = 0, len = this.length; i < len; i++){
23212                 if(this[i] == o) {
23213                     return i;
23214                 }
23215             }
23216             return -1;
23217         };
23218     }
23219     /**
23220      * The parent node for this node. @type Node
23221      */
23222     this.parentNode = null;
23223     /**
23224      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23225      */
23226     this.firstChild = null;
23227     /**
23228      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23229      */
23230     this.lastChild = null;
23231     /**
23232      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23233      */
23234     this.previousSibling = null;
23235     /**
23236      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23237      */
23238     this.nextSibling = null;
23239
23240     this.addEvents({
23241        /**
23242         * @event append
23243         * Fires when a new child node is appended
23244         * @param {Tree} tree The owner tree
23245         * @param {Node} this This node
23246         * @param {Node} node The newly appended node
23247         * @param {Number} index The index of the newly appended node
23248         */
23249        "append" : true,
23250        /**
23251         * @event remove
23252         * Fires when a child node is removed
23253         * @param {Tree} tree The owner tree
23254         * @param {Node} this This node
23255         * @param {Node} node The removed node
23256         */
23257        "remove" : true,
23258        /**
23259         * @event move
23260         * Fires when this node is moved to a new location in the tree
23261         * @param {Tree} tree The owner tree
23262         * @param {Node} this This node
23263         * @param {Node} oldParent The old parent of this node
23264         * @param {Node} newParent The new parent of this node
23265         * @param {Number} index The index it was moved to
23266         */
23267        "move" : true,
23268        /**
23269         * @event insert
23270         * Fires when a new child node is inserted.
23271         * @param {Tree} tree The owner tree
23272         * @param {Node} this This node
23273         * @param {Node} node The child node inserted
23274         * @param {Node} refNode The child node the node was inserted before
23275         */
23276        "insert" : true,
23277        /**
23278         * @event beforeappend
23279         * Fires before a new child is appended, return false to cancel the append.
23280         * @param {Tree} tree The owner tree
23281         * @param {Node} this This node
23282         * @param {Node} node The child node to be appended
23283         */
23284        "beforeappend" : true,
23285        /**
23286         * @event beforeremove
23287         * Fires before a child is removed, return false to cancel the remove.
23288         * @param {Tree} tree The owner tree
23289         * @param {Node} this This node
23290         * @param {Node} node The child node to be removed
23291         */
23292        "beforeremove" : true,
23293        /**
23294         * @event beforemove
23295         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23296         * @param {Tree} tree The owner tree
23297         * @param {Node} this This node
23298         * @param {Node} oldParent The parent of this node
23299         * @param {Node} newParent The new parent this node is moving to
23300         * @param {Number} index The index it is being moved to
23301         */
23302        "beforemove" : true,
23303        /**
23304         * @event beforeinsert
23305         * Fires before a new child is inserted, return false to cancel the insert.
23306         * @param {Tree} tree The owner tree
23307         * @param {Node} this This node
23308         * @param {Node} node The child node to be inserted
23309         * @param {Node} refNode The child node the node is being inserted before
23310         */
23311        "beforeinsert" : true
23312    });
23313     this.listeners = this.attributes.listeners;
23314     Roo.data.Node.superclass.constructor.call(this);
23315 };
23316
23317 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23318     fireEvent : function(evtName){
23319         // first do standard event for this node
23320         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23321             return false;
23322         }
23323         // then bubble it up to the tree if the event wasn't cancelled
23324         var ot = this.getOwnerTree();
23325         if(ot){
23326             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23327                 return false;
23328             }
23329         }
23330         return true;
23331     },
23332
23333     /**
23334      * Returns true if this node is a leaf
23335      * @return {Boolean}
23336      */
23337     isLeaf : function(){
23338         return this.leaf === true;
23339     },
23340
23341     // private
23342     setFirstChild : function(node){
23343         this.firstChild = node;
23344     },
23345
23346     //private
23347     setLastChild : function(node){
23348         this.lastChild = node;
23349     },
23350
23351
23352     /**
23353      * Returns true if this node is the last child of its parent
23354      * @return {Boolean}
23355      */
23356     isLast : function(){
23357        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23358     },
23359
23360     /**
23361      * Returns true if this node is the first child of its parent
23362      * @return {Boolean}
23363      */
23364     isFirst : function(){
23365        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23366     },
23367
23368     hasChildNodes : function(){
23369         return !this.isLeaf() && this.childNodes.length > 0;
23370     },
23371
23372     /**
23373      * Insert node(s) as the last child node of this node.
23374      * @param {Node/Array} node The node or Array of nodes to append
23375      * @return {Node} The appended node if single append, or null if an array was passed
23376      */
23377     appendChild : function(node){
23378         var multi = false;
23379         if(node instanceof Array){
23380             multi = node;
23381         }else if(arguments.length > 1){
23382             multi = arguments;
23383         }
23384         // if passed an array or multiple args do them one by one
23385         if(multi){
23386             for(var i = 0, len = multi.length; i < len; i++) {
23387                 this.appendChild(multi[i]);
23388             }
23389         }else{
23390             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23391                 return false;
23392             }
23393             var index = this.childNodes.length;
23394             var oldParent = node.parentNode;
23395             // it's a move, make sure we move it cleanly
23396             if(oldParent){
23397                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23398                     return false;
23399                 }
23400                 oldParent.removeChild(node);
23401             }
23402             index = this.childNodes.length;
23403             if(index == 0){
23404                 this.setFirstChild(node);
23405             }
23406             this.childNodes.push(node);
23407             node.parentNode = this;
23408             var ps = this.childNodes[index-1];
23409             if(ps){
23410                 node.previousSibling = ps;
23411                 ps.nextSibling = node;
23412             }else{
23413                 node.previousSibling = null;
23414             }
23415             node.nextSibling = null;
23416             this.setLastChild(node);
23417             node.setOwnerTree(this.getOwnerTree());
23418             this.fireEvent("append", this.ownerTree, this, node, index);
23419             if(oldParent){
23420                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23421             }
23422             return node;
23423         }
23424     },
23425
23426     /**
23427      * Removes a child node from this node.
23428      * @param {Node} node The node to remove
23429      * @return {Node} The removed node
23430      */
23431     removeChild : function(node){
23432         var index = this.childNodes.indexOf(node);
23433         if(index == -1){
23434             return false;
23435         }
23436         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23437             return false;
23438         }
23439
23440         // remove it from childNodes collection
23441         this.childNodes.splice(index, 1);
23442
23443         // update siblings
23444         if(node.previousSibling){
23445             node.previousSibling.nextSibling = node.nextSibling;
23446         }
23447         if(node.nextSibling){
23448             node.nextSibling.previousSibling = node.previousSibling;
23449         }
23450
23451         // update child refs
23452         if(this.firstChild == node){
23453             this.setFirstChild(node.nextSibling);
23454         }
23455         if(this.lastChild == node){
23456             this.setLastChild(node.previousSibling);
23457         }
23458
23459         node.setOwnerTree(null);
23460         // clear any references from the node
23461         node.parentNode = null;
23462         node.previousSibling = null;
23463         node.nextSibling = null;
23464         this.fireEvent("remove", this.ownerTree, this, node);
23465         return node;
23466     },
23467
23468     /**
23469      * Inserts the first node before the second node in this nodes childNodes collection.
23470      * @param {Node} node The node to insert
23471      * @param {Node} refNode The node to insert before (if null the node is appended)
23472      * @return {Node} The inserted node
23473      */
23474     insertBefore : function(node, refNode){
23475         if(!refNode){ // like standard Dom, refNode can be null for append
23476             return this.appendChild(node);
23477         }
23478         // nothing to do
23479         if(node == refNode){
23480             return false;
23481         }
23482
23483         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23484             return false;
23485         }
23486         var index = this.childNodes.indexOf(refNode);
23487         var oldParent = node.parentNode;
23488         var refIndex = index;
23489
23490         // when moving internally, indexes will change after remove
23491         if(oldParent == this && this.childNodes.indexOf(node) < index){
23492             refIndex--;
23493         }
23494
23495         // it's a move, make sure we move it cleanly
23496         if(oldParent){
23497             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23498                 return false;
23499             }
23500             oldParent.removeChild(node);
23501         }
23502         if(refIndex == 0){
23503             this.setFirstChild(node);
23504         }
23505         this.childNodes.splice(refIndex, 0, node);
23506         node.parentNode = this;
23507         var ps = this.childNodes[refIndex-1];
23508         if(ps){
23509             node.previousSibling = ps;
23510             ps.nextSibling = node;
23511         }else{
23512             node.previousSibling = null;
23513         }
23514         node.nextSibling = refNode;
23515         refNode.previousSibling = node;
23516         node.setOwnerTree(this.getOwnerTree());
23517         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23518         if(oldParent){
23519             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23520         }
23521         return node;
23522     },
23523
23524     /**
23525      * Returns the child node at the specified index.
23526      * @param {Number} index
23527      * @return {Node}
23528      */
23529     item : function(index){
23530         return this.childNodes[index];
23531     },
23532
23533     /**
23534      * Replaces one child node in this node with another.
23535      * @param {Node} newChild The replacement node
23536      * @param {Node} oldChild The node to replace
23537      * @return {Node} The replaced node
23538      */
23539     replaceChild : function(newChild, oldChild){
23540         this.insertBefore(newChild, oldChild);
23541         this.removeChild(oldChild);
23542         return oldChild;
23543     },
23544
23545     /**
23546      * Returns the index of a child node
23547      * @param {Node} node
23548      * @return {Number} The index of the node or -1 if it was not found
23549      */
23550     indexOf : function(child){
23551         return this.childNodes.indexOf(child);
23552     },
23553
23554     /**
23555      * Returns the tree this node is in.
23556      * @return {Tree}
23557      */
23558     getOwnerTree : function(){
23559         // if it doesn't have one, look for one
23560         if(!this.ownerTree){
23561             var p = this;
23562             while(p){
23563                 if(p.ownerTree){
23564                     this.ownerTree = p.ownerTree;
23565                     break;
23566                 }
23567                 p = p.parentNode;
23568             }
23569         }
23570         return this.ownerTree;
23571     },
23572
23573     /**
23574      * Returns depth of this node (the root node has a depth of 0)
23575      * @return {Number}
23576      */
23577     getDepth : function(){
23578         var depth = 0;
23579         var p = this;
23580         while(p.parentNode){
23581             ++depth;
23582             p = p.parentNode;
23583         }
23584         return depth;
23585     },
23586
23587     // private
23588     setOwnerTree : function(tree){
23589         // if it's move, we need to update everyone
23590         if(tree != this.ownerTree){
23591             if(this.ownerTree){
23592                 this.ownerTree.unregisterNode(this);
23593             }
23594             this.ownerTree = tree;
23595             var cs = this.childNodes;
23596             for(var i = 0, len = cs.length; i < len; i++) {
23597                 cs[i].setOwnerTree(tree);
23598             }
23599             if(tree){
23600                 tree.registerNode(this);
23601             }
23602         }
23603     },
23604
23605     /**
23606      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23607      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23608      * @return {String} The path
23609      */
23610     getPath : function(attr){
23611         attr = attr || "id";
23612         var p = this.parentNode;
23613         var b = [this.attributes[attr]];
23614         while(p){
23615             b.unshift(p.attributes[attr]);
23616             p = p.parentNode;
23617         }
23618         var sep = this.getOwnerTree().pathSeparator;
23619         return sep + b.join(sep);
23620     },
23621
23622     /**
23623      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23624      * function call will be the scope provided or the current node. The arguments to the function
23625      * will be the args provided or the current node. If the function returns false at any point,
23626      * the bubble is stopped.
23627      * @param {Function} fn The function to call
23628      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23629      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23630      */
23631     bubble : function(fn, scope, args){
23632         var p = this;
23633         while(p){
23634             if(fn.call(scope || p, args || p) === false){
23635                 break;
23636             }
23637             p = p.parentNode;
23638         }
23639     },
23640
23641     /**
23642      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23643      * function call will be the scope provided or the current node. The arguments to the function
23644      * will be the args provided or the current node. If the function returns false at any point,
23645      * the cascade is stopped on that branch.
23646      * @param {Function} fn The function to call
23647      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23648      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23649      */
23650     cascade : function(fn, scope, args){
23651         if(fn.call(scope || this, args || this) !== false){
23652             var cs = this.childNodes;
23653             for(var i = 0, len = cs.length; i < len; i++) {
23654                 cs[i].cascade(fn, scope, args);
23655             }
23656         }
23657     },
23658
23659     /**
23660      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23661      * function call will be the scope provided or the current node. The arguments to the function
23662      * will be the args provided or the current node. If the function returns false at any point,
23663      * the iteration stops.
23664      * @param {Function} fn The function to call
23665      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23666      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23667      */
23668     eachChild : function(fn, scope, args){
23669         var cs = this.childNodes;
23670         for(var i = 0, len = cs.length; i < len; i++) {
23671                 if(fn.call(scope || this, args || cs[i]) === false){
23672                     break;
23673                 }
23674         }
23675     },
23676
23677     /**
23678      * Finds the first child that has the attribute with the specified value.
23679      * @param {String} attribute The attribute name
23680      * @param {Mixed} value The value to search for
23681      * @return {Node} The found child or null if none was found
23682      */
23683     findChild : function(attribute, value){
23684         var cs = this.childNodes;
23685         for(var i = 0, len = cs.length; i < len; i++) {
23686                 if(cs[i].attributes[attribute] == value){
23687                     return cs[i];
23688                 }
23689         }
23690         return null;
23691     },
23692
23693     /**
23694      * Finds the first child by a custom function. The child matches if the function passed
23695      * returns true.
23696      * @param {Function} fn
23697      * @param {Object} scope (optional)
23698      * @return {Node} The found child or null if none was found
23699      */
23700     findChildBy : function(fn, scope){
23701         var cs = this.childNodes;
23702         for(var i = 0, len = cs.length; i < len; i++) {
23703                 if(fn.call(scope||cs[i], cs[i]) === true){
23704                     return cs[i];
23705                 }
23706         }
23707         return null;
23708     },
23709
23710     /**
23711      * Sorts this nodes children using the supplied sort function
23712      * @param {Function} fn
23713      * @param {Object} scope (optional)
23714      */
23715     sort : function(fn, scope){
23716         var cs = this.childNodes;
23717         var len = cs.length;
23718         if(len > 0){
23719             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23720             cs.sort(sortFn);
23721             for(var i = 0; i < len; i++){
23722                 var n = cs[i];
23723                 n.previousSibling = cs[i-1];
23724                 n.nextSibling = cs[i+1];
23725                 if(i == 0){
23726                     this.setFirstChild(n);
23727                 }
23728                 if(i == len-1){
23729                     this.setLastChild(n);
23730                 }
23731             }
23732         }
23733     },
23734
23735     /**
23736      * Returns true if this node is an ancestor (at any point) of the passed node.
23737      * @param {Node} node
23738      * @return {Boolean}
23739      */
23740     contains : function(node){
23741         return node.isAncestor(this);
23742     },
23743
23744     /**
23745      * Returns true if the passed node is an ancestor (at any point) of this node.
23746      * @param {Node} node
23747      * @return {Boolean}
23748      */
23749     isAncestor : function(node){
23750         var p = this.parentNode;
23751         while(p){
23752             if(p == node){
23753                 return true;
23754             }
23755             p = p.parentNode;
23756         }
23757         return false;
23758     },
23759
23760     toString : function(){
23761         return "[Node"+(this.id?" "+this.id:"")+"]";
23762     }
23763 });/*
23764  * Based on:
23765  * Ext JS Library 1.1.1
23766  * Copyright(c) 2006-2007, Ext JS, LLC.
23767  *
23768  * Originally Released Under LGPL - original licence link has changed is not relivant.
23769  *
23770  * Fork - LGPL
23771  * <script type="text/javascript">
23772  */
23773  (function(){ 
23774 /**
23775  * @class Roo.Layer
23776  * @extends Roo.Element
23777  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23778  * automatic maintaining of shadow/shim positions.
23779  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23780  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23781  * you can pass a string with a CSS class name. False turns off the shadow.
23782  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23783  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23784  * @cfg {String} cls CSS class to add to the element
23785  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23786  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23787  * @constructor
23788  * @param {Object} config An object with config options.
23789  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23790  */
23791
23792 Roo.Layer = function(config, existingEl){
23793     config = config || {};
23794     var dh = Roo.DomHelper;
23795     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23796     if(existingEl){
23797         this.dom = Roo.getDom(existingEl);
23798     }
23799     if(!this.dom){
23800         var o = config.dh || {tag: "div", cls: "x-layer"};
23801         this.dom = dh.append(pel, o);
23802     }
23803     if(config.cls){
23804         this.addClass(config.cls);
23805     }
23806     this.constrain = config.constrain !== false;
23807     this.visibilityMode = Roo.Element.VISIBILITY;
23808     if(config.id){
23809         this.id = this.dom.id = config.id;
23810     }else{
23811         this.id = Roo.id(this.dom);
23812     }
23813     this.zindex = config.zindex || this.getZIndex();
23814     this.position("absolute", this.zindex);
23815     if(config.shadow){
23816         this.shadowOffset = config.shadowOffset || 4;
23817         this.shadow = new Roo.Shadow({
23818             offset : this.shadowOffset,
23819             mode : config.shadow
23820         });
23821     }else{
23822         this.shadowOffset = 0;
23823     }
23824     this.useShim = config.shim !== false && Roo.useShims;
23825     this.useDisplay = config.useDisplay;
23826     this.hide();
23827 };
23828
23829 var supr = Roo.Element.prototype;
23830
23831 // shims are shared among layer to keep from having 100 iframes
23832 var shims = [];
23833
23834 Roo.extend(Roo.Layer, Roo.Element, {
23835
23836     getZIndex : function(){
23837         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23838     },
23839
23840     getShim : function(){
23841         if(!this.useShim){
23842             return null;
23843         }
23844         if(this.shim){
23845             return this.shim;
23846         }
23847         var shim = shims.shift();
23848         if(!shim){
23849             shim = this.createShim();
23850             shim.enableDisplayMode('block');
23851             shim.dom.style.display = 'none';
23852             shim.dom.style.visibility = 'visible';
23853         }
23854         var pn = this.dom.parentNode;
23855         if(shim.dom.parentNode != pn){
23856             pn.insertBefore(shim.dom, this.dom);
23857         }
23858         shim.setStyle('z-index', this.getZIndex()-2);
23859         this.shim = shim;
23860         return shim;
23861     },
23862
23863     hideShim : function(){
23864         if(this.shim){
23865             this.shim.setDisplayed(false);
23866             shims.push(this.shim);
23867             delete this.shim;
23868         }
23869     },
23870
23871     disableShadow : function(){
23872         if(this.shadow){
23873             this.shadowDisabled = true;
23874             this.shadow.hide();
23875             this.lastShadowOffset = this.shadowOffset;
23876             this.shadowOffset = 0;
23877         }
23878     },
23879
23880     enableShadow : function(show){
23881         if(this.shadow){
23882             this.shadowDisabled = false;
23883             this.shadowOffset = this.lastShadowOffset;
23884             delete this.lastShadowOffset;
23885             if(show){
23886                 this.sync(true);
23887             }
23888         }
23889     },
23890
23891     // private
23892     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23893     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23894     sync : function(doShow){
23895         var sw = this.shadow;
23896         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23897             var sh = this.getShim();
23898
23899             var w = this.getWidth(),
23900                 h = this.getHeight();
23901
23902             var l = this.getLeft(true),
23903                 t = this.getTop(true);
23904
23905             if(sw && !this.shadowDisabled){
23906                 if(doShow && !sw.isVisible()){
23907                     sw.show(this);
23908                 }else{
23909                     sw.realign(l, t, w, h);
23910                 }
23911                 if(sh){
23912                     if(doShow){
23913                        sh.show();
23914                     }
23915                     // fit the shim behind the shadow, so it is shimmed too
23916                     var a = sw.adjusts, s = sh.dom.style;
23917                     s.left = (Math.min(l, l+a.l))+"px";
23918                     s.top = (Math.min(t, t+a.t))+"px";
23919                     s.width = (w+a.w)+"px";
23920                     s.height = (h+a.h)+"px";
23921                 }
23922             }else if(sh){
23923                 if(doShow){
23924                    sh.show();
23925                 }
23926                 sh.setSize(w, h);
23927                 sh.setLeftTop(l, t);
23928             }
23929             
23930         }
23931     },
23932
23933     // private
23934     destroy : function(){
23935         this.hideShim();
23936         if(this.shadow){
23937             this.shadow.hide();
23938         }
23939         this.removeAllListeners();
23940         var pn = this.dom.parentNode;
23941         if(pn){
23942             pn.removeChild(this.dom);
23943         }
23944         Roo.Element.uncache(this.id);
23945     },
23946
23947     remove : function(){
23948         this.destroy();
23949     },
23950
23951     // private
23952     beginUpdate : function(){
23953         this.updating = true;
23954     },
23955
23956     // private
23957     endUpdate : function(){
23958         this.updating = false;
23959         this.sync(true);
23960     },
23961
23962     // private
23963     hideUnders : function(negOffset){
23964         if(this.shadow){
23965             this.shadow.hide();
23966         }
23967         this.hideShim();
23968     },
23969
23970     // private
23971     constrainXY : function(){
23972         if(this.constrain){
23973             var vw = Roo.lib.Dom.getViewWidth(),
23974                 vh = Roo.lib.Dom.getViewHeight();
23975             var s = Roo.get(document).getScroll();
23976
23977             var xy = this.getXY();
23978             var x = xy[0], y = xy[1];   
23979             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23980             // only move it if it needs it
23981             var moved = false;
23982             // first validate right/bottom
23983             if((x + w) > vw+s.left){
23984                 x = vw - w - this.shadowOffset;
23985                 moved = true;
23986             }
23987             if((y + h) > vh+s.top){
23988                 y = vh - h - this.shadowOffset;
23989                 moved = true;
23990             }
23991             // then make sure top/left isn't negative
23992             if(x < s.left){
23993                 x = s.left;
23994                 moved = true;
23995             }
23996             if(y < s.top){
23997                 y = s.top;
23998                 moved = true;
23999             }
24000             if(moved){
24001                 if(this.avoidY){
24002                     var ay = this.avoidY;
24003                     if(y <= ay && (y+h) >= ay){
24004                         y = ay-h-5;   
24005                     }
24006                 }
24007                 xy = [x, y];
24008                 this.storeXY(xy);
24009                 supr.setXY.call(this, xy);
24010                 this.sync();
24011             }
24012         }
24013     },
24014
24015     isVisible : function(){
24016         return this.visible;    
24017     },
24018
24019     // private
24020     showAction : function(){
24021         this.visible = true; // track visibility to prevent getStyle calls
24022         if(this.useDisplay === true){
24023             this.setDisplayed("");
24024         }else if(this.lastXY){
24025             supr.setXY.call(this, this.lastXY);
24026         }else if(this.lastLT){
24027             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
24028         }
24029     },
24030
24031     // private
24032     hideAction : function(){
24033         this.visible = false;
24034         if(this.useDisplay === true){
24035             this.setDisplayed(false);
24036         }else{
24037             this.setLeftTop(-10000,-10000);
24038         }
24039     },
24040
24041     // overridden Element method
24042     setVisible : function(v, a, d, c, e){
24043         if(v){
24044             this.showAction();
24045         }
24046         if(a && v){
24047             var cb = function(){
24048                 this.sync(true);
24049                 if(c){
24050                     c();
24051                 }
24052             }.createDelegate(this);
24053             supr.setVisible.call(this, true, true, d, cb, e);
24054         }else{
24055             if(!v){
24056                 this.hideUnders(true);
24057             }
24058             var cb = c;
24059             if(a){
24060                 cb = function(){
24061                     this.hideAction();
24062                     if(c){
24063                         c();
24064                     }
24065                 }.createDelegate(this);
24066             }
24067             supr.setVisible.call(this, v, a, d, cb, e);
24068             if(v){
24069                 this.sync(true);
24070             }else if(!a){
24071                 this.hideAction();
24072             }
24073         }
24074     },
24075
24076     storeXY : function(xy){
24077         delete this.lastLT;
24078         this.lastXY = xy;
24079     },
24080
24081     storeLeftTop : function(left, top){
24082         delete this.lastXY;
24083         this.lastLT = [left, top];
24084     },
24085
24086     // private
24087     beforeFx : function(){
24088         this.beforeAction();
24089         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24090     },
24091
24092     // private
24093     afterFx : function(){
24094         Roo.Layer.superclass.afterFx.apply(this, arguments);
24095         this.sync(this.isVisible());
24096     },
24097
24098     // private
24099     beforeAction : function(){
24100         if(!this.updating && this.shadow){
24101             this.shadow.hide();
24102         }
24103     },
24104
24105     // overridden Element method
24106     setLeft : function(left){
24107         this.storeLeftTop(left, this.getTop(true));
24108         supr.setLeft.apply(this, arguments);
24109         this.sync();
24110     },
24111
24112     setTop : function(top){
24113         this.storeLeftTop(this.getLeft(true), top);
24114         supr.setTop.apply(this, arguments);
24115         this.sync();
24116     },
24117
24118     setLeftTop : function(left, top){
24119         this.storeLeftTop(left, top);
24120         supr.setLeftTop.apply(this, arguments);
24121         this.sync();
24122     },
24123
24124     setXY : function(xy, a, d, c, e){
24125         this.fixDisplay();
24126         this.beforeAction();
24127         this.storeXY(xy);
24128         var cb = this.createCB(c);
24129         supr.setXY.call(this, xy, a, d, cb, e);
24130         if(!a){
24131             cb();
24132         }
24133     },
24134
24135     // private
24136     createCB : function(c){
24137         var el = this;
24138         return function(){
24139             el.constrainXY();
24140             el.sync(true);
24141             if(c){
24142                 c();
24143             }
24144         };
24145     },
24146
24147     // overridden Element method
24148     setX : function(x, a, d, c, e){
24149         this.setXY([x, this.getY()], a, d, c, e);
24150     },
24151
24152     // overridden Element method
24153     setY : function(y, a, d, c, e){
24154         this.setXY([this.getX(), y], a, d, c, e);
24155     },
24156
24157     // overridden Element method
24158     setSize : function(w, h, a, d, c, e){
24159         this.beforeAction();
24160         var cb = this.createCB(c);
24161         supr.setSize.call(this, w, h, a, d, cb, e);
24162         if(!a){
24163             cb();
24164         }
24165     },
24166
24167     // overridden Element method
24168     setWidth : function(w, a, d, c, e){
24169         this.beforeAction();
24170         var cb = this.createCB(c);
24171         supr.setWidth.call(this, w, a, d, cb, e);
24172         if(!a){
24173             cb();
24174         }
24175     },
24176
24177     // overridden Element method
24178     setHeight : function(h, a, d, c, e){
24179         this.beforeAction();
24180         var cb = this.createCB(c);
24181         supr.setHeight.call(this, h, a, d, cb, e);
24182         if(!a){
24183             cb();
24184         }
24185     },
24186
24187     // overridden Element method
24188     setBounds : function(x, y, w, h, a, d, c, e){
24189         this.beforeAction();
24190         var cb = this.createCB(c);
24191         if(!a){
24192             this.storeXY([x, y]);
24193             supr.setXY.call(this, [x, y]);
24194             supr.setSize.call(this, w, h, a, d, cb, e);
24195             cb();
24196         }else{
24197             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24198         }
24199         return this;
24200     },
24201     
24202     /**
24203      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24204      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24205      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24206      * @param {Number} zindex The new z-index to set
24207      * @return {this} The Layer
24208      */
24209     setZIndex : function(zindex){
24210         this.zindex = zindex;
24211         this.setStyle("z-index", zindex + 2);
24212         if(this.shadow){
24213             this.shadow.setZIndex(zindex + 1);
24214         }
24215         if(this.shim){
24216             this.shim.setStyle("z-index", zindex);
24217         }
24218     }
24219 });
24220 })();/*
24221  * Based on:
24222  * Ext JS Library 1.1.1
24223  * Copyright(c) 2006-2007, Ext JS, LLC.
24224  *
24225  * Originally Released Under LGPL - original licence link has changed is not relivant.
24226  *
24227  * Fork - LGPL
24228  * <script type="text/javascript">
24229  */
24230
24231
24232 /**
24233  * @class Roo.Shadow
24234  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24235  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24236  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24237  * @constructor
24238  * Create a new Shadow
24239  * @param {Object} config The config object
24240  */
24241 Roo.Shadow = function(config){
24242     Roo.apply(this, config);
24243     if(typeof this.mode != "string"){
24244         this.mode = this.defaultMode;
24245     }
24246     var o = this.offset, a = {h: 0};
24247     var rad = Math.floor(this.offset/2);
24248     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24249         case "drop":
24250             a.w = 0;
24251             a.l = a.t = o;
24252             a.t -= 1;
24253             if(Roo.isIE){
24254                 a.l -= this.offset + rad;
24255                 a.t -= this.offset + rad;
24256                 a.w -= rad;
24257                 a.h -= rad;
24258                 a.t += 1;
24259             }
24260         break;
24261         case "sides":
24262             a.w = (o*2);
24263             a.l = -o;
24264             a.t = o-1;
24265             if(Roo.isIE){
24266                 a.l -= (this.offset - rad);
24267                 a.t -= this.offset + rad;
24268                 a.l += 1;
24269                 a.w -= (this.offset - rad)*2;
24270                 a.w -= rad + 1;
24271                 a.h -= 1;
24272             }
24273         break;
24274         case "frame":
24275             a.w = a.h = (o*2);
24276             a.l = a.t = -o;
24277             a.t += 1;
24278             a.h -= 2;
24279             if(Roo.isIE){
24280                 a.l -= (this.offset - rad);
24281                 a.t -= (this.offset - rad);
24282                 a.l += 1;
24283                 a.w -= (this.offset + rad + 1);
24284                 a.h -= (this.offset + rad);
24285                 a.h += 1;
24286             }
24287         break;
24288     };
24289
24290     this.adjusts = a;
24291 };
24292
24293 Roo.Shadow.prototype = {
24294     /**
24295      * @cfg {String} mode
24296      * The shadow display mode.  Supports the following options:<br />
24297      * sides: Shadow displays on both sides and bottom only<br />
24298      * frame: Shadow displays equally on all four sides<br />
24299      * drop: Traditional bottom-right drop shadow (default)
24300      */
24301     /**
24302      * @cfg {String} offset
24303      * The number of pixels to offset the shadow from the element (defaults to 4)
24304      */
24305     offset: 4,
24306
24307     // private
24308     defaultMode: "drop",
24309
24310     /**
24311      * Displays the shadow under the target element
24312      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24313      */
24314     show : function(target){
24315         target = Roo.get(target);
24316         if(!this.el){
24317             this.el = Roo.Shadow.Pool.pull();
24318             if(this.el.dom.nextSibling != target.dom){
24319                 this.el.insertBefore(target);
24320             }
24321         }
24322         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24323         if(Roo.isIE){
24324             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24325         }
24326         this.realign(
24327             target.getLeft(true),
24328             target.getTop(true),
24329             target.getWidth(),
24330             target.getHeight()
24331         );
24332         this.el.dom.style.display = "block";
24333     },
24334
24335     /**
24336      * Returns true if the shadow is visible, else false
24337      */
24338     isVisible : function(){
24339         return this.el ? true : false;  
24340     },
24341
24342     /**
24343      * Direct alignment when values are already available. Show must be called at least once before
24344      * calling this method to ensure it is initialized.
24345      * @param {Number} left The target element left position
24346      * @param {Number} top The target element top position
24347      * @param {Number} width The target element width
24348      * @param {Number} height The target element height
24349      */
24350     realign : function(l, t, w, h){
24351         if(!this.el){
24352             return;
24353         }
24354         var a = this.adjusts, d = this.el.dom, s = d.style;
24355         var iea = 0;
24356         s.left = (l+a.l)+"px";
24357         s.top = (t+a.t)+"px";
24358         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24359  
24360         if(s.width != sws || s.height != shs){
24361             s.width = sws;
24362             s.height = shs;
24363             if(!Roo.isIE){
24364                 var cn = d.childNodes;
24365                 var sww = Math.max(0, (sw-12))+"px";
24366                 cn[0].childNodes[1].style.width = sww;
24367                 cn[1].childNodes[1].style.width = sww;
24368                 cn[2].childNodes[1].style.width = sww;
24369                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24370             }
24371         }
24372     },
24373
24374     /**
24375      * Hides this shadow
24376      */
24377     hide : function(){
24378         if(this.el){
24379             this.el.dom.style.display = "none";
24380             Roo.Shadow.Pool.push(this.el);
24381             delete this.el;
24382         }
24383     },
24384
24385     /**
24386      * Adjust the z-index of this shadow
24387      * @param {Number} zindex The new z-index
24388      */
24389     setZIndex : function(z){
24390         this.zIndex = z;
24391         if(this.el){
24392             this.el.setStyle("z-index", z);
24393         }
24394     }
24395 };
24396
24397 // Private utility class that manages the internal Shadow cache
24398 Roo.Shadow.Pool = function(){
24399     var p = [];
24400     var markup = Roo.isIE ?
24401                  '<div class="x-ie-shadow"></div>' :
24402                  '<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>';
24403     return {
24404         pull : function(){
24405             var sh = p.shift();
24406             if(!sh){
24407                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24408                 sh.autoBoxAdjust = false;
24409             }
24410             return sh;
24411         },
24412
24413         push : function(sh){
24414             p.push(sh);
24415         }
24416     };
24417 }();/*
24418  * Based on:
24419  * Ext JS Library 1.1.1
24420  * Copyright(c) 2006-2007, Ext JS, LLC.
24421  *
24422  * Originally Released Under LGPL - original licence link has changed is not relivant.
24423  *
24424  * Fork - LGPL
24425  * <script type="text/javascript">
24426  */
24427
24428
24429 /**
24430  * @class Roo.SplitBar
24431  * @extends Roo.util.Observable
24432  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24433  * <br><br>
24434  * Usage:
24435  * <pre><code>
24436 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24437                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24438 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24439 split.minSize = 100;
24440 split.maxSize = 600;
24441 split.animate = true;
24442 split.on('moved', splitterMoved);
24443 </code></pre>
24444  * @constructor
24445  * Create a new SplitBar
24446  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24447  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24448  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24449  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24450                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24451                         position of the SplitBar).
24452  */
24453 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24454     
24455     /** @private */
24456     this.el = Roo.get(dragElement, true);
24457     this.el.dom.unselectable = "on";
24458     /** @private */
24459     this.resizingEl = Roo.get(resizingElement, true);
24460
24461     /**
24462      * @private
24463      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24464      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24465      * @type Number
24466      */
24467     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24468     
24469     /**
24470      * The minimum size of the resizing element. (Defaults to 0)
24471      * @type Number
24472      */
24473     this.minSize = 0;
24474     
24475     /**
24476      * The maximum size of the resizing element. (Defaults to 2000)
24477      * @type Number
24478      */
24479     this.maxSize = 2000;
24480     
24481     /**
24482      * Whether to animate the transition to the new size
24483      * @type Boolean
24484      */
24485     this.animate = false;
24486     
24487     /**
24488      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24489      * @type Boolean
24490      */
24491     this.useShim = false;
24492     
24493     /** @private */
24494     this.shim = null;
24495     
24496     if(!existingProxy){
24497         /** @private */
24498         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24499     }else{
24500         this.proxy = Roo.get(existingProxy).dom;
24501     }
24502     /** @private */
24503     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24504     
24505     /** @private */
24506     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24507     
24508     /** @private */
24509     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24510     
24511     /** @private */
24512     this.dragSpecs = {};
24513     
24514     /**
24515      * @private The adapter to use to positon and resize elements
24516      */
24517     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24518     this.adapter.init(this);
24519     
24520     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24521         /** @private */
24522         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24523         this.el.addClass("x-splitbar-h");
24524     }else{
24525         /** @private */
24526         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24527         this.el.addClass("x-splitbar-v");
24528     }
24529     
24530     this.addEvents({
24531         /**
24532          * @event resize
24533          * Fires when the splitter is moved (alias for {@link #event-moved})
24534          * @param {Roo.SplitBar} this
24535          * @param {Number} newSize the new width or height
24536          */
24537         "resize" : true,
24538         /**
24539          * @event moved
24540          * Fires when the splitter is moved
24541          * @param {Roo.SplitBar} this
24542          * @param {Number} newSize the new width or height
24543          */
24544         "moved" : true,
24545         /**
24546          * @event beforeresize
24547          * Fires before the splitter is dragged
24548          * @param {Roo.SplitBar} this
24549          */
24550         "beforeresize" : true,
24551
24552         "beforeapply" : true
24553     });
24554
24555     Roo.util.Observable.call(this);
24556 };
24557
24558 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24559     onStartProxyDrag : function(x, y){
24560         this.fireEvent("beforeresize", this);
24561         if(!this.overlay){
24562             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24563             o.unselectable();
24564             o.enableDisplayMode("block");
24565             // all splitbars share the same overlay
24566             Roo.SplitBar.prototype.overlay = o;
24567         }
24568         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24569         this.overlay.show();
24570         Roo.get(this.proxy).setDisplayed("block");
24571         var size = this.adapter.getElementSize(this);
24572         this.activeMinSize = this.getMinimumSize();;
24573         this.activeMaxSize = this.getMaximumSize();;
24574         var c1 = size - this.activeMinSize;
24575         var c2 = Math.max(this.activeMaxSize - size, 0);
24576         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24577             this.dd.resetConstraints();
24578             this.dd.setXConstraint(
24579                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24580                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24581             );
24582             this.dd.setYConstraint(0, 0);
24583         }else{
24584             this.dd.resetConstraints();
24585             this.dd.setXConstraint(0, 0);
24586             this.dd.setYConstraint(
24587                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24588                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24589             );
24590          }
24591         this.dragSpecs.startSize = size;
24592         this.dragSpecs.startPoint = [x, y];
24593         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24594     },
24595     
24596     /** 
24597      * @private Called after the drag operation by the DDProxy
24598      */
24599     onEndProxyDrag : function(e){
24600         Roo.get(this.proxy).setDisplayed(false);
24601         var endPoint = Roo.lib.Event.getXY(e);
24602         if(this.overlay){
24603             this.overlay.hide();
24604         }
24605         var newSize;
24606         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24607             newSize = this.dragSpecs.startSize + 
24608                 (this.placement == Roo.SplitBar.LEFT ?
24609                     endPoint[0] - this.dragSpecs.startPoint[0] :
24610                     this.dragSpecs.startPoint[0] - endPoint[0]
24611                 );
24612         }else{
24613             newSize = this.dragSpecs.startSize + 
24614                 (this.placement == Roo.SplitBar.TOP ?
24615                     endPoint[1] - this.dragSpecs.startPoint[1] :
24616                     this.dragSpecs.startPoint[1] - endPoint[1]
24617                 );
24618         }
24619         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24620         if(newSize != this.dragSpecs.startSize){
24621             if(this.fireEvent('beforeapply', this, newSize) !== false){
24622                 this.adapter.setElementSize(this, newSize);
24623                 this.fireEvent("moved", this, newSize);
24624                 this.fireEvent("resize", this, newSize);
24625             }
24626         }
24627     },
24628     
24629     /**
24630      * Get the adapter this SplitBar uses
24631      * @return The adapter object
24632      */
24633     getAdapter : function(){
24634         return this.adapter;
24635     },
24636     
24637     /**
24638      * Set the adapter this SplitBar uses
24639      * @param {Object} adapter A SplitBar adapter object
24640      */
24641     setAdapter : function(adapter){
24642         this.adapter = adapter;
24643         this.adapter.init(this);
24644     },
24645     
24646     /**
24647      * Gets the minimum size for the resizing element
24648      * @return {Number} The minimum size
24649      */
24650     getMinimumSize : function(){
24651         return this.minSize;
24652     },
24653     
24654     /**
24655      * Sets the minimum size for the resizing element
24656      * @param {Number} minSize The minimum size
24657      */
24658     setMinimumSize : function(minSize){
24659         this.minSize = minSize;
24660     },
24661     
24662     /**
24663      * Gets the maximum size for the resizing element
24664      * @return {Number} The maximum size
24665      */
24666     getMaximumSize : function(){
24667         return this.maxSize;
24668     },
24669     
24670     /**
24671      * Sets the maximum size for the resizing element
24672      * @param {Number} maxSize The maximum size
24673      */
24674     setMaximumSize : function(maxSize){
24675         this.maxSize = maxSize;
24676     },
24677     
24678     /**
24679      * Sets the initialize size for the resizing element
24680      * @param {Number} size The initial size
24681      */
24682     setCurrentSize : function(size){
24683         var oldAnimate = this.animate;
24684         this.animate = false;
24685         this.adapter.setElementSize(this, size);
24686         this.animate = oldAnimate;
24687     },
24688     
24689     /**
24690      * Destroy this splitbar. 
24691      * @param {Boolean} removeEl True to remove the element
24692      */
24693     destroy : function(removeEl){
24694         if(this.shim){
24695             this.shim.remove();
24696         }
24697         this.dd.unreg();
24698         this.proxy.parentNode.removeChild(this.proxy);
24699         if(removeEl){
24700             this.el.remove();
24701         }
24702     }
24703 });
24704
24705 /**
24706  * @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.
24707  */
24708 Roo.SplitBar.createProxy = function(dir){
24709     var proxy = new Roo.Element(document.createElement("div"));
24710     proxy.unselectable();
24711     var cls = 'x-splitbar-proxy';
24712     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24713     document.body.appendChild(proxy.dom);
24714     return proxy.dom;
24715 };
24716
24717 /** 
24718  * @class Roo.SplitBar.BasicLayoutAdapter
24719  * Default Adapter. It assumes the splitter and resizing element are not positioned
24720  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24721  */
24722 Roo.SplitBar.BasicLayoutAdapter = function(){
24723 };
24724
24725 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24726     // do nothing for now
24727     init : function(s){
24728     
24729     },
24730     /**
24731      * Called before drag operations to get the current size of the resizing element. 
24732      * @param {Roo.SplitBar} s The SplitBar using this adapter
24733      */
24734      getElementSize : function(s){
24735         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24736             return s.resizingEl.getWidth();
24737         }else{
24738             return s.resizingEl.getHeight();
24739         }
24740     },
24741     
24742     /**
24743      * Called after drag operations to set the size of the resizing element.
24744      * @param {Roo.SplitBar} s The SplitBar using this adapter
24745      * @param {Number} newSize The new size to set
24746      * @param {Function} onComplete A function to be invoked when resizing is complete
24747      */
24748     setElementSize : function(s, newSize, onComplete){
24749         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24750             if(!s.animate){
24751                 s.resizingEl.setWidth(newSize);
24752                 if(onComplete){
24753                     onComplete(s, newSize);
24754                 }
24755             }else{
24756                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24757             }
24758         }else{
24759             
24760             if(!s.animate){
24761                 s.resizingEl.setHeight(newSize);
24762                 if(onComplete){
24763                     onComplete(s, newSize);
24764                 }
24765             }else{
24766                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24767             }
24768         }
24769     }
24770 };
24771
24772 /** 
24773  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24774  * @extends Roo.SplitBar.BasicLayoutAdapter
24775  * Adapter that  moves the splitter element to align with the resized sizing element. 
24776  * Used with an absolute positioned SplitBar.
24777  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24778  * document.body, make sure you assign an id to the body element.
24779  */
24780 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24781     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24782     this.container = Roo.get(container);
24783 };
24784
24785 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24786     init : function(s){
24787         this.basic.init(s);
24788     },
24789     
24790     getElementSize : function(s){
24791         return this.basic.getElementSize(s);
24792     },
24793     
24794     setElementSize : function(s, newSize, onComplete){
24795         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24796     },
24797     
24798     moveSplitter : function(s){
24799         var yes = Roo.SplitBar;
24800         switch(s.placement){
24801             case yes.LEFT:
24802                 s.el.setX(s.resizingEl.getRight());
24803                 break;
24804             case yes.RIGHT:
24805                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24806                 break;
24807             case yes.TOP:
24808                 s.el.setY(s.resizingEl.getBottom());
24809                 break;
24810             case yes.BOTTOM:
24811                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24812                 break;
24813         }
24814     }
24815 };
24816
24817 /**
24818  * Orientation constant - Create a vertical SplitBar
24819  * @static
24820  * @type Number
24821  */
24822 Roo.SplitBar.VERTICAL = 1;
24823
24824 /**
24825  * Orientation constant - Create a horizontal SplitBar
24826  * @static
24827  * @type Number
24828  */
24829 Roo.SplitBar.HORIZONTAL = 2;
24830
24831 /**
24832  * Placement constant - The resizing element is to the left of the splitter element
24833  * @static
24834  * @type Number
24835  */
24836 Roo.SplitBar.LEFT = 1;
24837
24838 /**
24839  * Placement constant - The resizing element is to the right of the splitter element
24840  * @static
24841  * @type Number
24842  */
24843 Roo.SplitBar.RIGHT = 2;
24844
24845 /**
24846  * Placement constant - The resizing element is positioned above the splitter element
24847  * @static
24848  * @type Number
24849  */
24850 Roo.SplitBar.TOP = 3;
24851
24852 /**
24853  * Placement constant - The resizing element is positioned under splitter element
24854  * @static
24855  * @type Number
24856  */
24857 Roo.SplitBar.BOTTOM = 4;
24858 /*
24859  * Based on:
24860  * Ext JS Library 1.1.1
24861  * Copyright(c) 2006-2007, Ext JS, LLC.
24862  *
24863  * Originally Released Under LGPL - original licence link has changed is not relivant.
24864  *
24865  * Fork - LGPL
24866  * <script type="text/javascript">
24867  */
24868
24869 /**
24870  * @class Roo.View
24871  * @extends Roo.util.Observable
24872  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24873  * This class also supports single and multi selection modes. <br>
24874  * Create a data model bound view:
24875  <pre><code>
24876  var store = new Roo.data.Store(...);
24877
24878  var view = new Roo.View({
24879     el : "my-element",
24880     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24881  
24882     singleSelect: true,
24883     selectedClass: "ydataview-selected",
24884     store: store
24885  });
24886
24887  // listen for node click?
24888  view.on("click", function(vw, index, node, e){
24889  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24890  });
24891
24892  // load XML data
24893  dataModel.load("foobar.xml");
24894  </code></pre>
24895  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24896  * <br><br>
24897  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24898  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24899  * 
24900  * Note: old style constructor is still suported (container, template, config)
24901  * 
24902  * @constructor
24903  * Create a new View
24904  * @param {Object} config The config object
24905  * 
24906  */
24907 Roo.View = function(config, depreciated_tpl, depreciated_config){
24908     
24909     this.parent = false;
24910     
24911     if (typeof(depreciated_tpl) == 'undefined') {
24912         // new way.. - universal constructor.
24913         Roo.apply(this, config);
24914         this.el  = Roo.get(this.el);
24915     } else {
24916         // old format..
24917         this.el  = Roo.get(config);
24918         this.tpl = depreciated_tpl;
24919         Roo.apply(this, depreciated_config);
24920     }
24921     this.wrapEl  = this.el.wrap().wrap();
24922     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24923     
24924     
24925     if(typeof(this.tpl) == "string"){
24926         this.tpl = new Roo.Template(this.tpl);
24927     } else {
24928         // support xtype ctors..
24929         this.tpl = new Roo.factory(this.tpl, Roo);
24930     }
24931     
24932     
24933     this.tpl.compile();
24934     
24935     /** @private */
24936     this.addEvents({
24937         /**
24938          * @event beforeclick
24939          * Fires before a click is processed. Returns false to cancel the default action.
24940          * @param {Roo.View} this
24941          * @param {Number} index The index of the target node
24942          * @param {HTMLElement} node The target node
24943          * @param {Roo.EventObject} e The raw event object
24944          */
24945             "beforeclick" : true,
24946         /**
24947          * @event click
24948          * Fires when a template node is clicked.
24949          * @param {Roo.View} this
24950          * @param {Number} index The index of the target node
24951          * @param {HTMLElement} node The target node
24952          * @param {Roo.EventObject} e The raw event object
24953          */
24954             "click" : true,
24955         /**
24956          * @event dblclick
24957          * Fires when a template node is double clicked.
24958          * @param {Roo.View} this
24959          * @param {Number} index The index of the target node
24960          * @param {HTMLElement} node The target node
24961          * @param {Roo.EventObject} e The raw event object
24962          */
24963             "dblclick" : true,
24964         /**
24965          * @event contextmenu
24966          * Fires when a template node is right clicked.
24967          * @param {Roo.View} this
24968          * @param {Number} index The index of the target node
24969          * @param {HTMLElement} node The target node
24970          * @param {Roo.EventObject} e The raw event object
24971          */
24972             "contextmenu" : true,
24973         /**
24974          * @event selectionchange
24975          * Fires when the selected nodes change.
24976          * @param {Roo.View} this
24977          * @param {Array} selections Array of the selected nodes
24978          */
24979             "selectionchange" : true,
24980     
24981         /**
24982          * @event beforeselect
24983          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24984          * @param {Roo.View} this
24985          * @param {HTMLElement} node The node to be selected
24986          * @param {Array} selections Array of currently selected nodes
24987          */
24988             "beforeselect" : true,
24989         /**
24990          * @event preparedata
24991          * Fires on every row to render, to allow you to change the data.
24992          * @param {Roo.View} this
24993          * @param {Object} data to be rendered (change this)
24994          */
24995           "preparedata" : true
24996           
24997           
24998         });
24999
25000
25001
25002     this.el.on({
25003         "click": this.onClick,
25004         "dblclick": this.onDblClick,
25005         "contextmenu": this.onContextMenu,
25006         scope:this
25007     });
25008
25009     this.selections = [];
25010     this.nodes = [];
25011     this.cmp = new Roo.CompositeElementLite([]);
25012     if(this.store){
25013         this.store = Roo.factory(this.store, Roo.data);
25014         this.setStore(this.store, true);
25015     }
25016     
25017     if ( this.footer && this.footer.xtype) {
25018            
25019          var fctr = this.wrapEl.appendChild(document.createElement("div"));
25020         
25021         this.footer.dataSource = this.store;
25022         this.footer.container = fctr;
25023         this.footer = Roo.factory(this.footer, Roo);
25024         fctr.insertFirst(this.el);
25025         
25026         // this is a bit insane - as the paging toolbar seems to detach the el..
25027 //        dom.parentNode.parentNode.parentNode
25028          // they get detached?
25029     }
25030     
25031     
25032     Roo.View.superclass.constructor.call(this);
25033     
25034     
25035 };
25036
25037 Roo.extend(Roo.View, Roo.util.Observable, {
25038     
25039      /**
25040      * @cfg {Roo.data.Store} store Data store to load data from.
25041      */
25042     store : false,
25043     
25044     /**
25045      * @cfg {String|Roo.Element} el The container element.
25046      */
25047     el : '',
25048     
25049     /**
25050      * @cfg {String|Roo.Template} tpl The template used by this View 
25051      */
25052     tpl : false,
25053     /**
25054      * @cfg {String} dataName the named area of the template to use as the data area
25055      *                          Works with domtemplates roo-name="name"
25056      */
25057     dataName: false,
25058     /**
25059      * @cfg {String} selectedClass The css class to add to selected nodes
25060      */
25061     selectedClass : "x-view-selected",
25062      /**
25063      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25064      */
25065     emptyText : "",
25066     
25067     /**
25068      * @cfg {String} text to display on mask (default Loading)
25069      */
25070     mask : false,
25071     /**
25072      * @cfg {Boolean} multiSelect Allow multiple selection
25073      */
25074     multiSelect : false,
25075     /**
25076      * @cfg {Boolean} singleSelect Allow single selection
25077      */
25078     singleSelect:  false,
25079     
25080     /**
25081      * @cfg {Boolean} toggleSelect - selecting 
25082      */
25083     toggleSelect : false,
25084     
25085     /**
25086      * @cfg {Boolean} tickable - selecting 
25087      */
25088     tickable : false,
25089     
25090     /**
25091      * Returns the element this view is bound to.
25092      * @return {Roo.Element}
25093      */
25094     getEl : function(){
25095         return this.wrapEl;
25096     },
25097     
25098     
25099
25100     /**
25101      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25102      */
25103     refresh : function(){
25104         //Roo.log('refresh');
25105         var t = this.tpl;
25106         
25107         // if we are using something like 'domtemplate', then
25108         // the what gets used is:
25109         // t.applySubtemplate(NAME, data, wrapping data..)
25110         // the outer template then get' applied with
25111         //     the store 'extra data'
25112         // and the body get's added to the
25113         //      roo-name="data" node?
25114         //      <span class='roo-tpl-{name}'></span> ?????
25115         
25116         
25117         
25118         this.clearSelections();
25119         this.el.update("");
25120         var html = [];
25121         var records = this.store.getRange();
25122         if(records.length < 1) {
25123             
25124             // is this valid??  = should it render a template??
25125             
25126             this.el.update(this.emptyText);
25127             return;
25128         }
25129         var el = this.el;
25130         if (this.dataName) {
25131             this.el.update(t.apply(this.store.meta)); //????
25132             el = this.el.child('.roo-tpl-' + this.dataName);
25133         }
25134         
25135         for(var i = 0, len = records.length; i < len; i++){
25136             var data = this.prepareData(records[i].data, i, records[i]);
25137             this.fireEvent("preparedata", this, data, i, records[i]);
25138             
25139             var d = Roo.apply({}, data);
25140             
25141             if(this.tickable){
25142                 Roo.apply(d, {'roo-id' : Roo.id()});
25143                 
25144                 var _this = this;
25145             
25146                 Roo.each(this.parent.item, function(item){
25147                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25148                         return;
25149                     }
25150                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25151                 });
25152             }
25153             
25154             html[html.length] = Roo.util.Format.trim(
25155                 this.dataName ?
25156                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25157                     t.apply(d)
25158             );
25159         }
25160         
25161         
25162         
25163         el.update(html.join(""));
25164         this.nodes = el.dom.childNodes;
25165         this.updateIndexes(0);
25166     },
25167     
25168
25169     /**
25170      * Function to override to reformat the data that is sent to
25171      * the template for each node.
25172      * DEPRICATED - use the preparedata event handler.
25173      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25174      * a JSON object for an UpdateManager bound view).
25175      */
25176     prepareData : function(data, index, record)
25177     {
25178         this.fireEvent("preparedata", this, data, index, record);
25179         return data;
25180     },
25181
25182     onUpdate : function(ds, record){
25183         // Roo.log('on update');   
25184         this.clearSelections();
25185         var index = this.store.indexOf(record);
25186         var n = this.nodes[index];
25187         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25188         n.parentNode.removeChild(n);
25189         this.updateIndexes(index, index);
25190     },
25191
25192     
25193     
25194 // --------- FIXME     
25195     onAdd : function(ds, records, index)
25196     {
25197         //Roo.log(['on Add', ds, records, index] );        
25198         this.clearSelections();
25199         if(this.nodes.length == 0){
25200             this.refresh();
25201             return;
25202         }
25203         var n = this.nodes[index];
25204         for(var i = 0, len = records.length; i < len; i++){
25205             var d = this.prepareData(records[i].data, i, records[i]);
25206             if(n){
25207                 this.tpl.insertBefore(n, d);
25208             }else{
25209                 
25210                 this.tpl.append(this.el, d);
25211             }
25212         }
25213         this.updateIndexes(index);
25214     },
25215
25216     onRemove : function(ds, record, index){
25217        // Roo.log('onRemove');
25218         this.clearSelections();
25219         var el = this.dataName  ?
25220             this.el.child('.roo-tpl-' + this.dataName) :
25221             this.el; 
25222         
25223         el.dom.removeChild(this.nodes[index]);
25224         this.updateIndexes(index);
25225     },
25226
25227     /**
25228      * Refresh an individual node.
25229      * @param {Number} index
25230      */
25231     refreshNode : function(index){
25232         this.onUpdate(this.store, this.store.getAt(index));
25233     },
25234
25235     updateIndexes : function(startIndex, endIndex){
25236         var ns = this.nodes;
25237         startIndex = startIndex || 0;
25238         endIndex = endIndex || ns.length - 1;
25239         for(var i = startIndex; i <= endIndex; i++){
25240             ns[i].nodeIndex = i;
25241         }
25242     },
25243
25244     /**
25245      * Changes the data store this view uses and refresh the view.
25246      * @param {Store} store
25247      */
25248     setStore : function(store, initial){
25249         if(!initial && this.store){
25250             this.store.un("datachanged", this.refresh);
25251             this.store.un("add", this.onAdd);
25252             this.store.un("remove", this.onRemove);
25253             this.store.un("update", this.onUpdate);
25254             this.store.un("clear", this.refresh);
25255             this.store.un("beforeload", this.onBeforeLoad);
25256             this.store.un("load", this.onLoad);
25257             this.store.un("loadexception", this.onLoad);
25258         }
25259         if(store){
25260           
25261             store.on("datachanged", this.refresh, this);
25262             store.on("add", this.onAdd, this);
25263             store.on("remove", this.onRemove, this);
25264             store.on("update", this.onUpdate, this);
25265             store.on("clear", this.refresh, this);
25266             store.on("beforeload", this.onBeforeLoad, this);
25267             store.on("load", this.onLoad, this);
25268             store.on("loadexception", this.onLoad, this);
25269         }
25270         
25271         if(store){
25272             this.refresh();
25273         }
25274     },
25275     /**
25276      * onbeforeLoad - masks the loading area.
25277      *
25278      */
25279     onBeforeLoad : function(store,opts)
25280     {
25281          //Roo.log('onBeforeLoad');   
25282         if (!opts.add) {
25283             this.el.update("");
25284         }
25285         this.el.mask(this.mask ? this.mask : "Loading" ); 
25286     },
25287     onLoad : function ()
25288     {
25289         this.el.unmask();
25290     },
25291     
25292
25293     /**
25294      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25295      * @param {HTMLElement} node
25296      * @return {HTMLElement} The template node
25297      */
25298     findItemFromChild : function(node){
25299         var el = this.dataName  ?
25300             this.el.child('.roo-tpl-' + this.dataName,true) :
25301             this.el.dom; 
25302         
25303         if(!node || node.parentNode == el){
25304                     return node;
25305             }
25306             var p = node.parentNode;
25307             while(p && p != el){
25308             if(p.parentNode == el){
25309                 return p;
25310             }
25311             p = p.parentNode;
25312         }
25313             return null;
25314     },
25315
25316     /** @ignore */
25317     onClick : function(e){
25318         var item = this.findItemFromChild(e.getTarget());
25319         if(item){
25320             var index = this.indexOf(item);
25321             if(this.onItemClick(item, index, e) !== false){
25322                 this.fireEvent("click", this, index, item, e);
25323             }
25324         }else{
25325             this.clearSelections();
25326         }
25327     },
25328
25329     /** @ignore */
25330     onContextMenu : function(e){
25331         var item = this.findItemFromChild(e.getTarget());
25332         if(item){
25333             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25334         }
25335     },
25336
25337     /** @ignore */
25338     onDblClick : function(e){
25339         var item = this.findItemFromChild(e.getTarget());
25340         if(item){
25341             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25342         }
25343     },
25344
25345     onItemClick : function(item, index, e)
25346     {
25347         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25348             return false;
25349         }
25350         if (this.toggleSelect) {
25351             var m = this.isSelected(item) ? 'unselect' : 'select';
25352             //Roo.log(m);
25353             var _t = this;
25354             _t[m](item, true, false);
25355             return true;
25356         }
25357         if(this.multiSelect || this.singleSelect){
25358             if(this.multiSelect && e.shiftKey && this.lastSelection){
25359                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25360             }else{
25361                 this.select(item, this.multiSelect && e.ctrlKey);
25362                 this.lastSelection = item;
25363             }
25364             
25365             if(!this.tickable){
25366                 e.preventDefault();
25367             }
25368             
25369         }
25370         return true;
25371     },
25372
25373     /**
25374      * Get the number of selected nodes.
25375      * @return {Number}
25376      */
25377     getSelectionCount : function(){
25378         return this.selections.length;
25379     },
25380
25381     /**
25382      * Get the currently selected nodes.
25383      * @return {Array} An array of HTMLElements
25384      */
25385     getSelectedNodes : function(){
25386         return this.selections;
25387     },
25388
25389     /**
25390      * Get the indexes of the selected nodes.
25391      * @return {Array}
25392      */
25393     getSelectedIndexes : function(){
25394         var indexes = [], s = this.selections;
25395         for(var i = 0, len = s.length; i < len; i++){
25396             indexes.push(s[i].nodeIndex);
25397         }
25398         return indexes;
25399     },
25400
25401     /**
25402      * Clear all selections
25403      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25404      */
25405     clearSelections : function(suppressEvent){
25406         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25407             this.cmp.elements = this.selections;
25408             this.cmp.removeClass(this.selectedClass);
25409             this.selections = [];
25410             if(!suppressEvent){
25411                 this.fireEvent("selectionchange", this, this.selections);
25412             }
25413         }
25414     },
25415
25416     /**
25417      * Returns true if the passed node is selected
25418      * @param {HTMLElement/Number} node The node or node index
25419      * @return {Boolean}
25420      */
25421     isSelected : function(node){
25422         var s = this.selections;
25423         if(s.length < 1){
25424             return false;
25425         }
25426         node = this.getNode(node);
25427         return s.indexOf(node) !== -1;
25428     },
25429
25430     /**
25431      * Selects nodes.
25432      * @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
25433      * @param {Boolean} keepExisting (optional) true to keep existing selections
25434      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25435      */
25436     select : function(nodeInfo, keepExisting, suppressEvent){
25437         if(nodeInfo instanceof Array){
25438             if(!keepExisting){
25439                 this.clearSelections(true);
25440             }
25441             for(var i = 0, len = nodeInfo.length; i < len; i++){
25442                 this.select(nodeInfo[i], true, true);
25443             }
25444             return;
25445         } 
25446         var node = this.getNode(nodeInfo);
25447         if(!node || this.isSelected(node)){
25448             return; // already selected.
25449         }
25450         if(!keepExisting){
25451             this.clearSelections(true);
25452         }
25453         
25454         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25455             Roo.fly(node).addClass(this.selectedClass);
25456             this.selections.push(node);
25457             if(!suppressEvent){
25458                 this.fireEvent("selectionchange", this, this.selections);
25459             }
25460         }
25461         
25462         
25463     },
25464       /**
25465      * Unselects nodes.
25466      * @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
25467      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25468      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25469      */
25470     unselect : function(nodeInfo, keepExisting, suppressEvent)
25471     {
25472         if(nodeInfo instanceof Array){
25473             Roo.each(this.selections, function(s) {
25474                 this.unselect(s, nodeInfo);
25475             }, this);
25476             return;
25477         }
25478         var node = this.getNode(nodeInfo);
25479         if(!node || !this.isSelected(node)){
25480             //Roo.log("not selected");
25481             return; // not selected.
25482         }
25483         // fireevent???
25484         var ns = [];
25485         Roo.each(this.selections, function(s) {
25486             if (s == node ) {
25487                 Roo.fly(node).removeClass(this.selectedClass);
25488
25489                 return;
25490             }
25491             ns.push(s);
25492         },this);
25493         
25494         this.selections= ns;
25495         this.fireEvent("selectionchange", this, this.selections);
25496     },
25497
25498     /**
25499      * Gets a template node.
25500      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25501      * @return {HTMLElement} The node or null if it wasn't found
25502      */
25503     getNode : function(nodeInfo){
25504         if(typeof nodeInfo == "string"){
25505             return document.getElementById(nodeInfo);
25506         }else if(typeof nodeInfo == "number"){
25507             return this.nodes[nodeInfo];
25508         }
25509         return nodeInfo;
25510     },
25511
25512     /**
25513      * Gets a range template nodes.
25514      * @param {Number} startIndex
25515      * @param {Number} endIndex
25516      * @return {Array} An array of nodes
25517      */
25518     getNodes : function(start, end){
25519         var ns = this.nodes;
25520         start = start || 0;
25521         end = typeof end == "undefined" ? ns.length - 1 : end;
25522         var nodes = [];
25523         if(start <= end){
25524             for(var i = start; i <= end; i++){
25525                 nodes.push(ns[i]);
25526             }
25527         } else{
25528             for(var i = start; i >= end; i--){
25529                 nodes.push(ns[i]);
25530             }
25531         }
25532         return nodes;
25533     },
25534
25535     /**
25536      * Finds the index of the passed node
25537      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25538      * @return {Number} The index of the node or -1
25539      */
25540     indexOf : function(node){
25541         node = this.getNode(node);
25542         if(typeof node.nodeIndex == "number"){
25543             return node.nodeIndex;
25544         }
25545         var ns = this.nodes;
25546         for(var i = 0, len = ns.length; i < len; i++){
25547             if(ns[i] == node){
25548                 return i;
25549             }
25550         }
25551         return -1;
25552     }
25553 });
25554 /*
25555  * Based on:
25556  * Ext JS Library 1.1.1
25557  * Copyright(c) 2006-2007, Ext JS, LLC.
25558  *
25559  * Originally Released Under LGPL - original licence link has changed is not relivant.
25560  *
25561  * Fork - LGPL
25562  * <script type="text/javascript">
25563  */
25564
25565 /**
25566  * @class Roo.JsonView
25567  * @extends Roo.View
25568  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25569 <pre><code>
25570 var view = new Roo.JsonView({
25571     container: "my-element",
25572     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25573     multiSelect: true, 
25574     jsonRoot: "data" 
25575 });
25576
25577 // listen for node click?
25578 view.on("click", function(vw, index, node, e){
25579     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25580 });
25581
25582 // direct load of JSON data
25583 view.load("foobar.php");
25584
25585 // Example from my blog list
25586 var tpl = new Roo.Template(
25587     '&lt;div class="entry"&gt;' +
25588     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25589     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25590     "&lt;/div&gt;&lt;hr /&gt;"
25591 );
25592
25593 var moreView = new Roo.JsonView({
25594     container :  "entry-list", 
25595     template : tpl,
25596     jsonRoot: "posts"
25597 });
25598 moreView.on("beforerender", this.sortEntries, this);
25599 moreView.load({
25600     url: "/blog/get-posts.php",
25601     params: "allposts=true",
25602     text: "Loading Blog Entries..."
25603 });
25604 </code></pre>
25605
25606 * Note: old code is supported with arguments : (container, template, config)
25607
25608
25609  * @constructor
25610  * Create a new JsonView
25611  * 
25612  * @param {Object} config The config object
25613  * 
25614  */
25615 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25616     
25617     
25618     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25619
25620     var um = this.el.getUpdateManager();
25621     um.setRenderer(this);
25622     um.on("update", this.onLoad, this);
25623     um.on("failure", this.onLoadException, this);
25624
25625     /**
25626      * @event beforerender
25627      * Fires before rendering of the downloaded JSON data.
25628      * @param {Roo.JsonView} this
25629      * @param {Object} data The JSON data loaded
25630      */
25631     /**
25632      * @event load
25633      * Fires when data is loaded.
25634      * @param {Roo.JsonView} this
25635      * @param {Object} data The JSON data loaded
25636      * @param {Object} response The raw Connect response object
25637      */
25638     /**
25639      * @event loadexception
25640      * Fires when loading fails.
25641      * @param {Roo.JsonView} this
25642      * @param {Object} response The raw Connect response object
25643      */
25644     this.addEvents({
25645         'beforerender' : true,
25646         'load' : true,
25647         'loadexception' : true
25648     });
25649 };
25650 Roo.extend(Roo.JsonView, Roo.View, {
25651     /**
25652      * @type {String} The root property in the loaded JSON object that contains the data
25653      */
25654     jsonRoot : "",
25655
25656     /**
25657      * Refreshes the view.
25658      */
25659     refresh : function(){
25660         this.clearSelections();
25661         this.el.update("");
25662         var html = [];
25663         var o = this.jsonData;
25664         if(o && o.length > 0){
25665             for(var i = 0, len = o.length; i < len; i++){
25666                 var data = this.prepareData(o[i], i, o);
25667                 html[html.length] = this.tpl.apply(data);
25668             }
25669         }else{
25670             html.push(this.emptyText);
25671         }
25672         this.el.update(html.join(""));
25673         this.nodes = this.el.dom.childNodes;
25674         this.updateIndexes(0);
25675     },
25676
25677     /**
25678      * 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.
25679      * @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:
25680      <pre><code>
25681      view.load({
25682          url: "your-url.php",
25683          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25684          callback: yourFunction,
25685          scope: yourObject, //(optional scope)
25686          discardUrl: false,
25687          nocache: false,
25688          text: "Loading...",
25689          timeout: 30,
25690          scripts: false
25691      });
25692      </code></pre>
25693      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25694      * 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.
25695      * @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}
25696      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25697      * @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.
25698      */
25699     load : function(){
25700         var um = this.el.getUpdateManager();
25701         um.update.apply(um, arguments);
25702     },
25703
25704     render : function(el, response){
25705         this.clearSelections();
25706         this.el.update("");
25707         var o;
25708         try{
25709             o = Roo.util.JSON.decode(response.responseText);
25710             if(this.jsonRoot){
25711                 
25712                 o = o[this.jsonRoot];
25713             }
25714         } catch(e){
25715         }
25716         /**
25717          * The current JSON data or null
25718          */
25719         this.jsonData = o;
25720         this.beforeRender();
25721         this.refresh();
25722     },
25723
25724 /**
25725  * Get the number of records in the current JSON dataset
25726  * @return {Number}
25727  */
25728     getCount : function(){
25729         return this.jsonData ? this.jsonData.length : 0;
25730     },
25731
25732 /**
25733  * Returns the JSON object for the specified node(s)
25734  * @param {HTMLElement/Array} node The node or an array of nodes
25735  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25736  * you get the JSON object for the node
25737  */
25738     getNodeData : function(node){
25739         if(node instanceof Array){
25740             var data = [];
25741             for(var i = 0, len = node.length; i < len; i++){
25742                 data.push(this.getNodeData(node[i]));
25743             }
25744             return data;
25745         }
25746         return this.jsonData[this.indexOf(node)] || null;
25747     },
25748
25749     beforeRender : function(){
25750         this.snapshot = this.jsonData;
25751         if(this.sortInfo){
25752             this.sort.apply(this, this.sortInfo);
25753         }
25754         this.fireEvent("beforerender", this, this.jsonData);
25755     },
25756
25757     onLoad : function(el, o){
25758         this.fireEvent("load", this, this.jsonData, o);
25759     },
25760
25761     onLoadException : function(el, o){
25762         this.fireEvent("loadexception", this, o);
25763     },
25764
25765 /**
25766  * Filter the data by a specific property.
25767  * @param {String} property A property on your JSON objects
25768  * @param {String/RegExp} value Either string that the property values
25769  * should start with, or a RegExp to test against the property
25770  */
25771     filter : function(property, value){
25772         if(this.jsonData){
25773             var data = [];
25774             var ss = this.snapshot;
25775             if(typeof value == "string"){
25776                 var vlen = value.length;
25777                 if(vlen == 0){
25778                     this.clearFilter();
25779                     return;
25780                 }
25781                 value = value.toLowerCase();
25782                 for(var i = 0, len = ss.length; i < len; i++){
25783                     var o = ss[i];
25784                     if(o[property].substr(0, vlen).toLowerCase() == value){
25785                         data.push(o);
25786                     }
25787                 }
25788             } else if(value.exec){ // regex?
25789                 for(var i = 0, len = ss.length; i < len; i++){
25790                     var o = ss[i];
25791                     if(value.test(o[property])){
25792                         data.push(o);
25793                     }
25794                 }
25795             } else{
25796                 return;
25797             }
25798             this.jsonData = data;
25799             this.refresh();
25800         }
25801     },
25802
25803 /**
25804  * Filter by a function. The passed function will be called with each
25805  * object in the current dataset. If the function returns true the value is kept,
25806  * otherwise it is filtered.
25807  * @param {Function} fn
25808  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25809  */
25810     filterBy : function(fn, scope){
25811         if(this.jsonData){
25812             var data = [];
25813             var ss = this.snapshot;
25814             for(var i = 0, len = ss.length; i < len; i++){
25815                 var o = ss[i];
25816                 if(fn.call(scope || this, o)){
25817                     data.push(o);
25818                 }
25819             }
25820             this.jsonData = data;
25821             this.refresh();
25822         }
25823     },
25824
25825 /**
25826  * Clears the current filter.
25827  */
25828     clearFilter : function(){
25829         if(this.snapshot && this.jsonData != this.snapshot){
25830             this.jsonData = this.snapshot;
25831             this.refresh();
25832         }
25833     },
25834
25835
25836 /**
25837  * Sorts the data for this view and refreshes it.
25838  * @param {String} property A property on your JSON objects to sort on
25839  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25840  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25841  */
25842     sort : function(property, dir, sortType){
25843         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25844         if(this.jsonData){
25845             var p = property;
25846             var dsc = dir && dir.toLowerCase() == "desc";
25847             var f = function(o1, o2){
25848                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25849                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25850                 ;
25851                 if(v1 < v2){
25852                     return dsc ? +1 : -1;
25853                 } else if(v1 > v2){
25854                     return dsc ? -1 : +1;
25855                 } else{
25856                     return 0;
25857                 }
25858             };
25859             this.jsonData.sort(f);
25860             this.refresh();
25861             if(this.jsonData != this.snapshot){
25862                 this.snapshot.sort(f);
25863             }
25864         }
25865     }
25866 });/*
25867  * Based on:
25868  * Ext JS Library 1.1.1
25869  * Copyright(c) 2006-2007, Ext JS, LLC.
25870  *
25871  * Originally Released Under LGPL - original licence link has changed is not relivant.
25872  *
25873  * Fork - LGPL
25874  * <script type="text/javascript">
25875  */
25876  
25877
25878 /**
25879  * @class Roo.ColorPalette
25880  * @extends Roo.Component
25881  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25882  * Here's an example of typical usage:
25883  * <pre><code>
25884 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25885 cp.render('my-div');
25886
25887 cp.on('select', function(palette, selColor){
25888     // do something with selColor
25889 });
25890 </code></pre>
25891  * @constructor
25892  * Create a new ColorPalette
25893  * @param {Object} config The config object
25894  */
25895 Roo.ColorPalette = function(config){
25896     Roo.ColorPalette.superclass.constructor.call(this, config);
25897     this.addEvents({
25898         /**
25899              * @event select
25900              * Fires when a color is selected
25901              * @param {ColorPalette} this
25902              * @param {String} color The 6-digit color hex code (without the # symbol)
25903              */
25904         select: true
25905     });
25906
25907     if(this.handler){
25908         this.on("select", this.handler, this.scope, true);
25909     }
25910 };
25911 Roo.extend(Roo.ColorPalette, Roo.Component, {
25912     /**
25913      * @cfg {String} itemCls
25914      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25915      */
25916     itemCls : "x-color-palette",
25917     /**
25918      * @cfg {String} value
25919      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25920      * the hex codes are case-sensitive.
25921      */
25922     value : null,
25923     clickEvent:'click',
25924     // private
25925     ctype: "Roo.ColorPalette",
25926
25927     /**
25928      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25929      */
25930     allowReselect : false,
25931
25932     /**
25933      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25934      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25935      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25936      * of colors with the width setting until the box is symmetrical.</p>
25937      * <p>You can override individual colors if needed:</p>
25938      * <pre><code>
25939 var cp = new Roo.ColorPalette();
25940 cp.colors[0] = "FF0000";  // change the first box to red
25941 </code></pre>
25942
25943 Or you can provide a custom array of your own for complete control:
25944 <pre><code>
25945 var cp = new Roo.ColorPalette();
25946 cp.colors = ["000000", "993300", "333300"];
25947 </code></pre>
25948      * @type Array
25949      */
25950     colors : [
25951         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25952         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25953         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25954         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25955         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25956     ],
25957
25958     // private
25959     onRender : function(container, position){
25960         var t = new Roo.MasterTemplate(
25961             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25962         );
25963         var c = this.colors;
25964         for(var i = 0, len = c.length; i < len; i++){
25965             t.add([c[i]]);
25966         }
25967         var el = document.createElement("div");
25968         el.className = this.itemCls;
25969         t.overwrite(el);
25970         container.dom.insertBefore(el, position);
25971         this.el = Roo.get(el);
25972         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25973         if(this.clickEvent != 'click'){
25974             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25975         }
25976     },
25977
25978     // private
25979     afterRender : function(){
25980         Roo.ColorPalette.superclass.afterRender.call(this);
25981         if(this.value){
25982             var s = this.value;
25983             this.value = null;
25984             this.select(s);
25985         }
25986     },
25987
25988     // private
25989     handleClick : function(e, t){
25990         e.preventDefault();
25991         if(!this.disabled){
25992             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25993             this.select(c.toUpperCase());
25994         }
25995     },
25996
25997     /**
25998      * Selects the specified color in the palette (fires the select event)
25999      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
26000      */
26001     select : function(color){
26002         color = color.replace("#", "");
26003         if(color != this.value || this.allowReselect){
26004             var el = this.el;
26005             if(this.value){
26006                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
26007             }
26008             el.child("a.color-"+color).addClass("x-color-palette-sel");
26009             this.value = color;
26010             this.fireEvent("select", this, color);
26011         }
26012     }
26013 });/*
26014  * Based on:
26015  * Ext JS Library 1.1.1
26016  * Copyright(c) 2006-2007, Ext JS, LLC.
26017  *
26018  * Originally Released Under LGPL - original licence link has changed is not relivant.
26019  *
26020  * Fork - LGPL
26021  * <script type="text/javascript">
26022  */
26023  
26024 /**
26025  * @class Roo.DatePicker
26026  * @extends Roo.Component
26027  * Simple date picker class.
26028  * @constructor
26029  * Create a new DatePicker
26030  * @param {Object} config The config object
26031  */
26032 Roo.DatePicker = function(config){
26033     Roo.DatePicker.superclass.constructor.call(this, config);
26034
26035     this.value = config && config.value ?
26036                  config.value.clearTime() : new Date().clearTime();
26037
26038     this.addEvents({
26039         /**
26040              * @event select
26041              * Fires when a date is selected
26042              * @param {DatePicker} this
26043              * @param {Date} date The selected date
26044              */
26045         'select': true,
26046         /**
26047              * @event monthchange
26048              * Fires when the displayed month changes 
26049              * @param {DatePicker} this
26050              * @param {Date} date The selected month
26051              */
26052         'monthchange': true
26053     });
26054
26055     if(this.handler){
26056         this.on("select", this.handler,  this.scope || this);
26057     }
26058     // build the disabledDatesRE
26059     if(!this.disabledDatesRE && this.disabledDates){
26060         var dd = this.disabledDates;
26061         var re = "(?:";
26062         for(var i = 0; i < dd.length; i++){
26063             re += dd[i];
26064             if(i != dd.length-1) {
26065                 re += "|";
26066             }
26067         }
26068         this.disabledDatesRE = new RegExp(re + ")");
26069     }
26070 };
26071
26072 Roo.extend(Roo.DatePicker, Roo.Component, {
26073     /**
26074      * @cfg {String} todayText
26075      * The text to display on the button that selects the current date (defaults to "Today")
26076      */
26077     todayText : "Today",
26078     /**
26079      * @cfg {String} okText
26080      * The text to display on the ok button
26081      */
26082     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26083     /**
26084      * @cfg {String} cancelText
26085      * The text to display on the cancel button
26086      */
26087     cancelText : "Cancel",
26088     /**
26089      * @cfg {String} todayTip
26090      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26091      */
26092     todayTip : "{0} (Spacebar)",
26093     /**
26094      * @cfg {Date} minDate
26095      * Minimum allowable date (JavaScript date object, defaults to null)
26096      */
26097     minDate : null,
26098     /**
26099      * @cfg {Date} maxDate
26100      * Maximum allowable date (JavaScript date object, defaults to null)
26101      */
26102     maxDate : null,
26103     /**
26104      * @cfg {String} minText
26105      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26106      */
26107     minText : "This date is before the minimum date",
26108     /**
26109      * @cfg {String} maxText
26110      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26111      */
26112     maxText : "This date is after the maximum date",
26113     /**
26114      * @cfg {String} format
26115      * The default date format string which can be overriden for localization support.  The format must be
26116      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26117      */
26118     format : "m/d/y",
26119     /**
26120      * @cfg {Array} disabledDays
26121      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26122      */
26123     disabledDays : null,
26124     /**
26125      * @cfg {String} disabledDaysText
26126      * The tooltip to display when the date falls on a disabled day (defaults to "")
26127      */
26128     disabledDaysText : "",
26129     /**
26130      * @cfg {RegExp} disabledDatesRE
26131      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26132      */
26133     disabledDatesRE : null,
26134     /**
26135      * @cfg {String} disabledDatesText
26136      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26137      */
26138     disabledDatesText : "",
26139     /**
26140      * @cfg {Boolean} constrainToViewport
26141      * True to constrain the date picker to the viewport (defaults to true)
26142      */
26143     constrainToViewport : true,
26144     /**
26145      * @cfg {Array} monthNames
26146      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26147      */
26148     monthNames : Date.monthNames,
26149     /**
26150      * @cfg {Array} dayNames
26151      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26152      */
26153     dayNames : Date.dayNames,
26154     /**
26155      * @cfg {String} nextText
26156      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26157      */
26158     nextText: 'Next Month (Control+Right)',
26159     /**
26160      * @cfg {String} prevText
26161      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26162      */
26163     prevText: 'Previous Month (Control+Left)',
26164     /**
26165      * @cfg {String} monthYearText
26166      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26167      */
26168     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26169     /**
26170      * @cfg {Number} startDay
26171      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26172      */
26173     startDay : 0,
26174     /**
26175      * @cfg {Bool} showClear
26176      * Show a clear button (usefull for date form elements that can be blank.)
26177      */
26178     
26179     showClear: false,
26180     
26181     /**
26182      * Sets the value of the date field
26183      * @param {Date} value The date to set
26184      */
26185     setValue : function(value){
26186         var old = this.value;
26187         
26188         if (typeof(value) == 'string') {
26189          
26190             value = Date.parseDate(value, this.format);
26191         }
26192         if (!value) {
26193             value = new Date();
26194         }
26195         
26196         this.value = value.clearTime(true);
26197         if(this.el){
26198             this.update(this.value);
26199         }
26200     },
26201
26202     /**
26203      * Gets the current selected value of the date field
26204      * @return {Date} The selected date
26205      */
26206     getValue : function(){
26207         return this.value;
26208     },
26209
26210     // private
26211     focus : function(){
26212         if(this.el){
26213             this.update(this.activeDate);
26214         }
26215     },
26216
26217     // privateval
26218     onRender : function(container, position){
26219         
26220         var m = [
26221              '<table cellspacing="0">',
26222                 '<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>',
26223                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26224         var dn = this.dayNames;
26225         for(var i = 0; i < 7; i++){
26226             var d = this.startDay+i;
26227             if(d > 6){
26228                 d = d-7;
26229             }
26230             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26231         }
26232         m[m.length] = "</tr></thead><tbody><tr>";
26233         for(var i = 0; i < 42; i++) {
26234             if(i % 7 == 0 && i != 0){
26235                 m[m.length] = "</tr><tr>";
26236             }
26237             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26238         }
26239         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26240             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26241
26242         var el = document.createElement("div");
26243         el.className = "x-date-picker";
26244         el.innerHTML = m.join("");
26245
26246         container.dom.insertBefore(el, position);
26247
26248         this.el = Roo.get(el);
26249         this.eventEl = Roo.get(el.firstChild);
26250
26251         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26252             handler: this.showPrevMonth,
26253             scope: this,
26254             preventDefault:true,
26255             stopDefault:true
26256         });
26257
26258         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26259             handler: this.showNextMonth,
26260             scope: this,
26261             preventDefault:true,
26262             stopDefault:true
26263         });
26264
26265         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26266
26267         this.monthPicker = this.el.down('div.x-date-mp');
26268         this.monthPicker.enableDisplayMode('block');
26269         
26270         var kn = new Roo.KeyNav(this.eventEl, {
26271             "left" : function(e){
26272                 e.ctrlKey ?
26273                     this.showPrevMonth() :
26274                     this.update(this.activeDate.add("d", -1));
26275             },
26276
26277             "right" : function(e){
26278                 e.ctrlKey ?
26279                     this.showNextMonth() :
26280                     this.update(this.activeDate.add("d", 1));
26281             },
26282
26283             "up" : function(e){
26284                 e.ctrlKey ?
26285                     this.showNextYear() :
26286                     this.update(this.activeDate.add("d", -7));
26287             },
26288
26289             "down" : function(e){
26290                 e.ctrlKey ?
26291                     this.showPrevYear() :
26292                     this.update(this.activeDate.add("d", 7));
26293             },
26294
26295             "pageUp" : function(e){
26296                 this.showNextMonth();
26297             },
26298
26299             "pageDown" : function(e){
26300                 this.showPrevMonth();
26301             },
26302
26303             "enter" : function(e){
26304                 e.stopPropagation();
26305                 return true;
26306             },
26307
26308             scope : this
26309         });
26310
26311         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26312
26313         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26314
26315         this.el.unselectable();
26316         
26317         this.cells = this.el.select("table.x-date-inner tbody td");
26318         this.textNodes = this.el.query("table.x-date-inner tbody span");
26319
26320         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26321             text: "&#160;",
26322             tooltip: this.monthYearText
26323         });
26324
26325         this.mbtn.on('click', this.showMonthPicker, this);
26326         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26327
26328
26329         var today = (new Date()).dateFormat(this.format);
26330         
26331         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26332         if (this.showClear) {
26333             baseTb.add( new Roo.Toolbar.Fill());
26334         }
26335         baseTb.add({
26336             text: String.format(this.todayText, today),
26337             tooltip: String.format(this.todayTip, today),
26338             handler: this.selectToday,
26339             scope: this
26340         });
26341         
26342         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26343             
26344         //});
26345         if (this.showClear) {
26346             
26347             baseTb.add( new Roo.Toolbar.Fill());
26348             baseTb.add({
26349                 text: '&#160;',
26350                 cls: 'x-btn-icon x-btn-clear',
26351                 handler: function() {
26352                     //this.value = '';
26353                     this.fireEvent("select", this, '');
26354                 },
26355                 scope: this
26356             });
26357         }
26358         
26359         
26360         if(Roo.isIE){
26361             this.el.repaint();
26362         }
26363         this.update(this.value);
26364     },
26365
26366     createMonthPicker : function(){
26367         if(!this.monthPicker.dom.firstChild){
26368             var buf = ['<table border="0" cellspacing="0">'];
26369             for(var i = 0; i < 6; i++){
26370                 buf.push(
26371                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26372                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26373                     i == 0 ?
26374                     '<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>' :
26375                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26376                 );
26377             }
26378             buf.push(
26379                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26380                     this.okText,
26381                     '</button><button type="button" class="x-date-mp-cancel">',
26382                     this.cancelText,
26383                     '</button></td></tr>',
26384                 '</table>'
26385             );
26386             this.monthPicker.update(buf.join(''));
26387             this.monthPicker.on('click', this.onMonthClick, this);
26388             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26389
26390             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26391             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26392
26393             this.mpMonths.each(function(m, a, i){
26394                 i += 1;
26395                 if((i%2) == 0){
26396                     m.dom.xmonth = 5 + Math.round(i * .5);
26397                 }else{
26398                     m.dom.xmonth = Math.round((i-1) * .5);
26399                 }
26400             });
26401         }
26402     },
26403
26404     showMonthPicker : function(){
26405         this.createMonthPicker();
26406         var size = this.el.getSize();
26407         this.monthPicker.setSize(size);
26408         this.monthPicker.child('table').setSize(size);
26409
26410         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26411         this.updateMPMonth(this.mpSelMonth);
26412         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26413         this.updateMPYear(this.mpSelYear);
26414
26415         this.monthPicker.slideIn('t', {duration:.2});
26416     },
26417
26418     updateMPYear : function(y){
26419         this.mpyear = y;
26420         var ys = this.mpYears.elements;
26421         for(var i = 1; i <= 10; i++){
26422             var td = ys[i-1], y2;
26423             if((i%2) == 0){
26424                 y2 = y + Math.round(i * .5);
26425                 td.firstChild.innerHTML = y2;
26426                 td.xyear = y2;
26427             }else{
26428                 y2 = y - (5-Math.round(i * .5));
26429                 td.firstChild.innerHTML = y2;
26430                 td.xyear = y2;
26431             }
26432             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26433         }
26434     },
26435
26436     updateMPMonth : function(sm){
26437         this.mpMonths.each(function(m, a, i){
26438             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26439         });
26440     },
26441
26442     selectMPMonth: function(m){
26443         
26444     },
26445
26446     onMonthClick : function(e, t){
26447         e.stopEvent();
26448         var el = new Roo.Element(t), pn;
26449         if(el.is('button.x-date-mp-cancel')){
26450             this.hideMonthPicker();
26451         }
26452         else if(el.is('button.x-date-mp-ok')){
26453             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26454             this.hideMonthPicker();
26455         }
26456         else if(pn = el.up('td.x-date-mp-month', 2)){
26457             this.mpMonths.removeClass('x-date-mp-sel');
26458             pn.addClass('x-date-mp-sel');
26459             this.mpSelMonth = pn.dom.xmonth;
26460         }
26461         else if(pn = el.up('td.x-date-mp-year', 2)){
26462             this.mpYears.removeClass('x-date-mp-sel');
26463             pn.addClass('x-date-mp-sel');
26464             this.mpSelYear = pn.dom.xyear;
26465         }
26466         else if(el.is('a.x-date-mp-prev')){
26467             this.updateMPYear(this.mpyear-10);
26468         }
26469         else if(el.is('a.x-date-mp-next')){
26470             this.updateMPYear(this.mpyear+10);
26471         }
26472     },
26473
26474     onMonthDblClick : function(e, t){
26475         e.stopEvent();
26476         var el = new Roo.Element(t), pn;
26477         if(pn = el.up('td.x-date-mp-month', 2)){
26478             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26479             this.hideMonthPicker();
26480         }
26481         else if(pn = el.up('td.x-date-mp-year', 2)){
26482             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26483             this.hideMonthPicker();
26484         }
26485     },
26486
26487     hideMonthPicker : function(disableAnim){
26488         if(this.monthPicker){
26489             if(disableAnim === true){
26490                 this.monthPicker.hide();
26491             }else{
26492                 this.monthPicker.slideOut('t', {duration:.2});
26493             }
26494         }
26495     },
26496
26497     // private
26498     showPrevMonth : function(e){
26499         this.update(this.activeDate.add("mo", -1));
26500     },
26501
26502     // private
26503     showNextMonth : function(e){
26504         this.update(this.activeDate.add("mo", 1));
26505     },
26506
26507     // private
26508     showPrevYear : function(){
26509         this.update(this.activeDate.add("y", -1));
26510     },
26511
26512     // private
26513     showNextYear : function(){
26514         this.update(this.activeDate.add("y", 1));
26515     },
26516
26517     // private
26518     handleMouseWheel : function(e){
26519         var delta = e.getWheelDelta();
26520         if(delta > 0){
26521             this.showPrevMonth();
26522             e.stopEvent();
26523         } else if(delta < 0){
26524             this.showNextMonth();
26525             e.stopEvent();
26526         }
26527     },
26528
26529     // private
26530     handleDateClick : function(e, t){
26531         e.stopEvent();
26532         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26533             this.setValue(new Date(t.dateValue));
26534             this.fireEvent("select", this, this.value);
26535         }
26536     },
26537
26538     // private
26539     selectToday : function(){
26540         this.setValue(new Date().clearTime());
26541         this.fireEvent("select", this, this.value);
26542     },
26543
26544     // private
26545     update : function(date)
26546     {
26547         var vd = this.activeDate;
26548         this.activeDate = date;
26549         if(vd && this.el){
26550             var t = date.getTime();
26551             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26552                 this.cells.removeClass("x-date-selected");
26553                 this.cells.each(function(c){
26554                    if(c.dom.firstChild.dateValue == t){
26555                        c.addClass("x-date-selected");
26556                        setTimeout(function(){
26557                             try{c.dom.firstChild.focus();}catch(e){}
26558                        }, 50);
26559                        return false;
26560                    }
26561                 });
26562                 return;
26563             }
26564         }
26565         
26566         var days = date.getDaysInMonth();
26567         var firstOfMonth = date.getFirstDateOfMonth();
26568         var startingPos = firstOfMonth.getDay()-this.startDay;
26569
26570         if(startingPos <= this.startDay){
26571             startingPos += 7;
26572         }
26573
26574         var pm = date.add("mo", -1);
26575         var prevStart = pm.getDaysInMonth()-startingPos;
26576
26577         var cells = this.cells.elements;
26578         var textEls = this.textNodes;
26579         days += startingPos;
26580
26581         // convert everything to numbers so it's fast
26582         var day = 86400000;
26583         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26584         var today = new Date().clearTime().getTime();
26585         var sel = date.clearTime().getTime();
26586         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26587         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26588         var ddMatch = this.disabledDatesRE;
26589         var ddText = this.disabledDatesText;
26590         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26591         var ddaysText = this.disabledDaysText;
26592         var format = this.format;
26593
26594         var setCellClass = function(cal, cell){
26595             cell.title = "";
26596             var t = d.getTime();
26597             cell.firstChild.dateValue = t;
26598             if(t == today){
26599                 cell.className += " x-date-today";
26600                 cell.title = cal.todayText;
26601             }
26602             if(t == sel){
26603                 cell.className += " x-date-selected";
26604                 setTimeout(function(){
26605                     try{cell.firstChild.focus();}catch(e){}
26606                 }, 50);
26607             }
26608             // disabling
26609             if(t < min) {
26610                 cell.className = " x-date-disabled";
26611                 cell.title = cal.minText;
26612                 return;
26613             }
26614             if(t > max) {
26615                 cell.className = " x-date-disabled";
26616                 cell.title = cal.maxText;
26617                 return;
26618             }
26619             if(ddays){
26620                 if(ddays.indexOf(d.getDay()) != -1){
26621                     cell.title = ddaysText;
26622                     cell.className = " x-date-disabled";
26623                 }
26624             }
26625             if(ddMatch && format){
26626                 var fvalue = d.dateFormat(format);
26627                 if(ddMatch.test(fvalue)){
26628                     cell.title = ddText.replace("%0", fvalue);
26629                     cell.className = " x-date-disabled";
26630                 }
26631             }
26632         };
26633
26634         var i = 0;
26635         for(; i < startingPos; i++) {
26636             textEls[i].innerHTML = (++prevStart);
26637             d.setDate(d.getDate()+1);
26638             cells[i].className = "x-date-prevday";
26639             setCellClass(this, cells[i]);
26640         }
26641         for(; i < days; i++){
26642             intDay = i - startingPos + 1;
26643             textEls[i].innerHTML = (intDay);
26644             d.setDate(d.getDate()+1);
26645             cells[i].className = "x-date-active";
26646             setCellClass(this, cells[i]);
26647         }
26648         var extraDays = 0;
26649         for(; i < 42; i++) {
26650              textEls[i].innerHTML = (++extraDays);
26651              d.setDate(d.getDate()+1);
26652              cells[i].className = "x-date-nextday";
26653              setCellClass(this, cells[i]);
26654         }
26655
26656         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26657         this.fireEvent('monthchange', this, date);
26658         
26659         if(!this.internalRender){
26660             var main = this.el.dom.firstChild;
26661             var w = main.offsetWidth;
26662             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26663             Roo.fly(main).setWidth(w);
26664             this.internalRender = true;
26665             // opera does not respect the auto grow header center column
26666             // then, after it gets a width opera refuses to recalculate
26667             // without a second pass
26668             if(Roo.isOpera && !this.secondPass){
26669                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26670                 this.secondPass = true;
26671                 this.update.defer(10, this, [date]);
26672             }
26673         }
26674         
26675         
26676     }
26677 });        /*
26678  * Based on:
26679  * Ext JS Library 1.1.1
26680  * Copyright(c) 2006-2007, Ext JS, LLC.
26681  *
26682  * Originally Released Under LGPL - original licence link has changed is not relivant.
26683  *
26684  * Fork - LGPL
26685  * <script type="text/javascript">
26686  */
26687 /**
26688  * @class Roo.TabPanel
26689  * @extends Roo.util.Observable
26690  * A lightweight tab container.
26691  * <br><br>
26692  * Usage:
26693  * <pre><code>
26694 // basic tabs 1, built from existing content
26695 var tabs = new Roo.TabPanel("tabs1");
26696 tabs.addTab("script", "View Script");
26697 tabs.addTab("markup", "View Markup");
26698 tabs.activate("script");
26699
26700 // more advanced tabs, built from javascript
26701 var jtabs = new Roo.TabPanel("jtabs");
26702 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26703
26704 // set up the UpdateManager
26705 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26706 var updater = tab2.getUpdateManager();
26707 updater.setDefaultUrl("ajax1.htm");
26708 tab2.on('activate', updater.refresh, updater, true);
26709
26710 // Use setUrl for Ajax loading
26711 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26712 tab3.setUrl("ajax2.htm", null, true);
26713
26714 // Disabled tab
26715 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26716 tab4.disable();
26717
26718 jtabs.activate("jtabs-1");
26719  * </code></pre>
26720  * @constructor
26721  * Create a new TabPanel.
26722  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26723  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26724  */
26725 Roo.TabPanel = function(container, config){
26726     /**
26727     * The container element for this TabPanel.
26728     * @type Roo.Element
26729     */
26730     this.el = Roo.get(container, true);
26731     if(config){
26732         if(typeof config == "boolean"){
26733             this.tabPosition = config ? "bottom" : "top";
26734         }else{
26735             Roo.apply(this, config);
26736         }
26737     }
26738     if(this.tabPosition == "bottom"){
26739         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26740         this.el.addClass("x-tabs-bottom");
26741     }
26742     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26743     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26744     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26745     if(Roo.isIE){
26746         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26747     }
26748     if(this.tabPosition != "bottom"){
26749         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26750          * @type Roo.Element
26751          */
26752         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26753         this.el.addClass("x-tabs-top");
26754     }
26755     this.items = [];
26756
26757     this.bodyEl.setStyle("position", "relative");
26758
26759     this.active = null;
26760     this.activateDelegate = this.activate.createDelegate(this);
26761
26762     this.addEvents({
26763         /**
26764          * @event tabchange
26765          * Fires when the active tab changes
26766          * @param {Roo.TabPanel} this
26767          * @param {Roo.TabPanelItem} activePanel The new active tab
26768          */
26769         "tabchange": true,
26770         /**
26771          * @event beforetabchange
26772          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26773          * @param {Roo.TabPanel} this
26774          * @param {Object} e Set cancel to true on this object to cancel the tab change
26775          * @param {Roo.TabPanelItem} tab The tab being changed to
26776          */
26777         "beforetabchange" : true
26778     });
26779
26780     Roo.EventManager.onWindowResize(this.onResize, this);
26781     this.cpad = this.el.getPadding("lr");
26782     this.hiddenCount = 0;
26783
26784
26785     // toolbar on the tabbar support...
26786     if (this.toolbar) {
26787         var tcfg = this.toolbar;
26788         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26789         this.toolbar = new Roo.Toolbar(tcfg);
26790         if (Roo.isSafari) {
26791             var tbl = tcfg.container.child('table', true);
26792             tbl.setAttribute('width', '100%');
26793         }
26794         
26795     }
26796    
26797
26798
26799     Roo.TabPanel.superclass.constructor.call(this);
26800 };
26801
26802 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26803     /*
26804      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26805      */
26806     tabPosition : "top",
26807     /*
26808      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26809      */
26810     currentTabWidth : 0,
26811     /*
26812      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26813      */
26814     minTabWidth : 40,
26815     /*
26816      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26817      */
26818     maxTabWidth : 250,
26819     /*
26820      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26821      */
26822     preferredTabWidth : 175,
26823     /*
26824      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26825      */
26826     resizeTabs : false,
26827     /*
26828      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26829      */
26830     monitorResize : true,
26831     /*
26832      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26833      */
26834     toolbar : false,
26835
26836     /**
26837      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26838      * @param {String} id The id of the div to use <b>or create</b>
26839      * @param {String} text The text for the tab
26840      * @param {String} content (optional) Content to put in the TabPanelItem body
26841      * @param {Boolean} closable (optional) True to create a close icon on the tab
26842      * @return {Roo.TabPanelItem} The created TabPanelItem
26843      */
26844     addTab : function(id, text, content, closable){
26845         var item = new Roo.TabPanelItem(this, id, text, closable);
26846         this.addTabItem(item);
26847         if(content){
26848             item.setContent(content);
26849         }
26850         return item;
26851     },
26852
26853     /**
26854      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26855      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26856      * @return {Roo.TabPanelItem}
26857      */
26858     getTab : function(id){
26859         return this.items[id];
26860     },
26861
26862     /**
26863      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26864      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26865      */
26866     hideTab : function(id){
26867         var t = this.items[id];
26868         if(!t.isHidden()){
26869            t.setHidden(true);
26870            this.hiddenCount++;
26871            this.autoSizeTabs();
26872         }
26873     },
26874
26875     /**
26876      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26877      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26878      */
26879     unhideTab : function(id){
26880         var t = this.items[id];
26881         if(t.isHidden()){
26882            t.setHidden(false);
26883            this.hiddenCount--;
26884            this.autoSizeTabs();
26885         }
26886     },
26887
26888     /**
26889      * Adds an existing {@link Roo.TabPanelItem}.
26890      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26891      */
26892     addTabItem : function(item){
26893         this.items[item.id] = item;
26894         this.items.push(item);
26895         if(this.resizeTabs){
26896            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26897            this.autoSizeTabs();
26898         }else{
26899             item.autoSize();
26900         }
26901     },
26902
26903     /**
26904      * Removes a {@link Roo.TabPanelItem}.
26905      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26906      */
26907     removeTab : function(id){
26908         var items = this.items;
26909         var tab = items[id];
26910         if(!tab) { return; }
26911         var index = items.indexOf(tab);
26912         if(this.active == tab && items.length > 1){
26913             var newTab = this.getNextAvailable(index);
26914             if(newTab) {
26915                 newTab.activate();
26916             }
26917         }
26918         this.stripEl.dom.removeChild(tab.pnode.dom);
26919         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26920             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26921         }
26922         items.splice(index, 1);
26923         delete this.items[tab.id];
26924         tab.fireEvent("close", tab);
26925         tab.purgeListeners();
26926         this.autoSizeTabs();
26927     },
26928
26929     getNextAvailable : function(start){
26930         var items = this.items;
26931         var index = start;
26932         // look for a next tab that will slide over to
26933         // replace the one being removed
26934         while(index < items.length){
26935             var item = items[++index];
26936             if(item && !item.isHidden()){
26937                 return item;
26938             }
26939         }
26940         // if one isn't found select the previous tab (on the left)
26941         index = start;
26942         while(index >= 0){
26943             var item = items[--index];
26944             if(item && !item.isHidden()){
26945                 return item;
26946             }
26947         }
26948         return null;
26949     },
26950
26951     /**
26952      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26953      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26954      */
26955     disableTab : function(id){
26956         var tab = this.items[id];
26957         if(tab && this.active != tab){
26958             tab.disable();
26959         }
26960     },
26961
26962     /**
26963      * Enables a {@link Roo.TabPanelItem} that is disabled.
26964      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26965      */
26966     enableTab : function(id){
26967         var tab = this.items[id];
26968         tab.enable();
26969     },
26970
26971     /**
26972      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26973      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26974      * @return {Roo.TabPanelItem} The TabPanelItem.
26975      */
26976     activate : function(id){
26977         var tab = this.items[id];
26978         if(!tab){
26979             return null;
26980         }
26981         if(tab == this.active || tab.disabled){
26982             return tab;
26983         }
26984         var e = {};
26985         this.fireEvent("beforetabchange", this, e, tab);
26986         if(e.cancel !== true && !tab.disabled){
26987             if(this.active){
26988                 this.active.hide();
26989             }
26990             this.active = this.items[id];
26991             this.active.show();
26992             this.fireEvent("tabchange", this, this.active);
26993         }
26994         return tab;
26995     },
26996
26997     /**
26998      * Gets the active {@link Roo.TabPanelItem}.
26999      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
27000      */
27001     getActiveTab : function(){
27002         return this.active;
27003     },
27004
27005     /**
27006      * Updates the tab body element to fit the height of the container element
27007      * for overflow scrolling
27008      * @param {Number} targetHeight (optional) Override the starting height from the elements height
27009      */
27010     syncHeight : function(targetHeight){
27011         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
27012         var bm = this.bodyEl.getMargins();
27013         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
27014         this.bodyEl.setHeight(newHeight);
27015         return newHeight;
27016     },
27017
27018     onResize : function(){
27019         if(this.monitorResize){
27020             this.autoSizeTabs();
27021         }
27022     },
27023
27024     /**
27025      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
27026      */
27027     beginUpdate : function(){
27028         this.updating = true;
27029     },
27030
27031     /**
27032      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
27033      */
27034     endUpdate : function(){
27035         this.updating = false;
27036         this.autoSizeTabs();
27037     },
27038
27039     /**
27040      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27041      */
27042     autoSizeTabs : function(){
27043         var count = this.items.length;
27044         var vcount = count - this.hiddenCount;
27045         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
27046             return;
27047         }
27048         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27049         var availWidth = Math.floor(w / vcount);
27050         var b = this.stripBody;
27051         if(b.getWidth() > w){
27052             var tabs = this.items;
27053             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27054             if(availWidth < this.minTabWidth){
27055                 /*if(!this.sleft){    // incomplete scrolling code
27056                     this.createScrollButtons();
27057                 }
27058                 this.showScroll();
27059                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27060             }
27061         }else{
27062             if(this.currentTabWidth < this.preferredTabWidth){
27063                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27064             }
27065         }
27066     },
27067
27068     /**
27069      * Returns the number of tabs in this TabPanel.
27070      * @return {Number}
27071      */
27072      getCount : function(){
27073          return this.items.length;
27074      },
27075
27076     /**
27077      * Resizes all the tabs to the passed width
27078      * @param {Number} The new width
27079      */
27080     setTabWidth : function(width){
27081         this.currentTabWidth = width;
27082         for(var i = 0, len = this.items.length; i < len; i++) {
27083                 if(!this.items[i].isHidden()) {
27084                 this.items[i].setWidth(width);
27085             }
27086         }
27087     },
27088
27089     /**
27090      * Destroys this TabPanel
27091      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27092      */
27093     destroy : function(removeEl){
27094         Roo.EventManager.removeResizeListener(this.onResize, this);
27095         for(var i = 0, len = this.items.length; i < len; i++){
27096             this.items[i].purgeListeners();
27097         }
27098         if(removeEl === true){
27099             this.el.update("");
27100             this.el.remove();
27101         }
27102     }
27103 });
27104
27105 /**
27106  * @class Roo.TabPanelItem
27107  * @extends Roo.util.Observable
27108  * Represents an individual item (tab plus body) in a TabPanel.
27109  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27110  * @param {String} id The id of this TabPanelItem
27111  * @param {String} text The text for the tab of this TabPanelItem
27112  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27113  */
27114 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27115     /**
27116      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27117      * @type Roo.TabPanel
27118      */
27119     this.tabPanel = tabPanel;
27120     /**
27121      * The id for this TabPanelItem
27122      * @type String
27123      */
27124     this.id = id;
27125     /** @private */
27126     this.disabled = false;
27127     /** @private */
27128     this.text = text;
27129     /** @private */
27130     this.loaded = false;
27131     this.closable = closable;
27132
27133     /**
27134      * The body element for this TabPanelItem.
27135      * @type Roo.Element
27136      */
27137     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27138     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27139     this.bodyEl.setStyle("display", "block");
27140     this.bodyEl.setStyle("zoom", "1");
27141     this.hideAction();
27142
27143     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27144     /** @private */
27145     this.el = Roo.get(els.el, true);
27146     this.inner = Roo.get(els.inner, true);
27147     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27148     this.pnode = Roo.get(els.el.parentNode, true);
27149     this.el.on("mousedown", this.onTabMouseDown, this);
27150     this.el.on("click", this.onTabClick, this);
27151     /** @private */
27152     if(closable){
27153         var c = Roo.get(els.close, true);
27154         c.dom.title = this.closeText;
27155         c.addClassOnOver("close-over");
27156         c.on("click", this.closeClick, this);
27157      }
27158
27159     this.addEvents({
27160          /**
27161          * @event activate
27162          * Fires when this tab becomes the active tab.
27163          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27164          * @param {Roo.TabPanelItem} this
27165          */
27166         "activate": true,
27167         /**
27168          * @event beforeclose
27169          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27170          * @param {Roo.TabPanelItem} this
27171          * @param {Object} e Set cancel to true on this object to cancel the close.
27172          */
27173         "beforeclose": true,
27174         /**
27175          * @event close
27176          * Fires when this tab is closed.
27177          * @param {Roo.TabPanelItem} this
27178          */
27179          "close": true,
27180         /**
27181          * @event deactivate
27182          * Fires when this tab is no longer the active tab.
27183          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27184          * @param {Roo.TabPanelItem} this
27185          */
27186          "deactivate" : true
27187     });
27188     this.hidden = false;
27189
27190     Roo.TabPanelItem.superclass.constructor.call(this);
27191 };
27192
27193 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27194     purgeListeners : function(){
27195        Roo.util.Observable.prototype.purgeListeners.call(this);
27196        this.el.removeAllListeners();
27197     },
27198     /**
27199      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27200      */
27201     show : function(){
27202         this.pnode.addClass("on");
27203         this.showAction();
27204         if(Roo.isOpera){
27205             this.tabPanel.stripWrap.repaint();
27206         }
27207         this.fireEvent("activate", this.tabPanel, this);
27208     },
27209
27210     /**
27211      * Returns true if this tab is the active tab.
27212      * @return {Boolean}
27213      */
27214     isActive : function(){
27215         return this.tabPanel.getActiveTab() == this;
27216     },
27217
27218     /**
27219      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27220      */
27221     hide : function(){
27222         this.pnode.removeClass("on");
27223         this.hideAction();
27224         this.fireEvent("deactivate", this.tabPanel, this);
27225     },
27226
27227     hideAction : function(){
27228         this.bodyEl.hide();
27229         this.bodyEl.setStyle("position", "absolute");
27230         this.bodyEl.setLeft("-20000px");
27231         this.bodyEl.setTop("-20000px");
27232     },
27233
27234     showAction : function(){
27235         this.bodyEl.setStyle("position", "relative");
27236         this.bodyEl.setTop("");
27237         this.bodyEl.setLeft("");
27238         this.bodyEl.show();
27239     },
27240
27241     /**
27242      * Set the tooltip for the tab.
27243      * @param {String} tooltip The tab's tooltip
27244      */
27245     setTooltip : function(text){
27246         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27247             this.textEl.dom.qtip = text;
27248             this.textEl.dom.removeAttribute('title');
27249         }else{
27250             this.textEl.dom.title = text;
27251         }
27252     },
27253
27254     onTabClick : function(e){
27255         e.preventDefault();
27256         this.tabPanel.activate(this.id);
27257     },
27258
27259     onTabMouseDown : function(e){
27260         e.preventDefault();
27261         this.tabPanel.activate(this.id);
27262     },
27263
27264     getWidth : function(){
27265         return this.inner.getWidth();
27266     },
27267
27268     setWidth : function(width){
27269         var iwidth = width - this.pnode.getPadding("lr");
27270         this.inner.setWidth(iwidth);
27271         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27272         this.pnode.setWidth(width);
27273     },
27274
27275     /**
27276      * Show or hide the tab
27277      * @param {Boolean} hidden True to hide or false to show.
27278      */
27279     setHidden : function(hidden){
27280         this.hidden = hidden;
27281         this.pnode.setStyle("display", hidden ? "none" : "");
27282     },
27283
27284     /**
27285      * Returns true if this tab is "hidden"
27286      * @return {Boolean}
27287      */
27288     isHidden : function(){
27289         return this.hidden;
27290     },
27291
27292     /**
27293      * Returns the text for this tab
27294      * @return {String}
27295      */
27296     getText : function(){
27297         return this.text;
27298     },
27299
27300     autoSize : function(){
27301         //this.el.beginMeasure();
27302         this.textEl.setWidth(1);
27303         /*
27304          *  #2804 [new] Tabs in Roojs
27305          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27306          */
27307         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27308         //this.el.endMeasure();
27309     },
27310
27311     /**
27312      * Sets the text for the tab (Note: this also sets the tooltip text)
27313      * @param {String} text The tab's text and tooltip
27314      */
27315     setText : function(text){
27316         this.text = text;
27317         this.textEl.update(text);
27318         this.setTooltip(text);
27319         if(!this.tabPanel.resizeTabs){
27320             this.autoSize();
27321         }
27322     },
27323     /**
27324      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27325      */
27326     activate : function(){
27327         this.tabPanel.activate(this.id);
27328     },
27329
27330     /**
27331      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27332      */
27333     disable : function(){
27334         if(this.tabPanel.active != this){
27335             this.disabled = true;
27336             this.pnode.addClass("disabled");
27337         }
27338     },
27339
27340     /**
27341      * Enables this TabPanelItem if it was previously disabled.
27342      */
27343     enable : function(){
27344         this.disabled = false;
27345         this.pnode.removeClass("disabled");
27346     },
27347
27348     /**
27349      * Sets the content for this TabPanelItem.
27350      * @param {String} content The content
27351      * @param {Boolean} loadScripts true to look for and load scripts
27352      */
27353     setContent : function(content, loadScripts){
27354         this.bodyEl.update(content, loadScripts);
27355     },
27356
27357     /**
27358      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27359      * @return {Roo.UpdateManager} The UpdateManager
27360      */
27361     getUpdateManager : function(){
27362         return this.bodyEl.getUpdateManager();
27363     },
27364
27365     /**
27366      * Set a URL to be used to load the content for this TabPanelItem.
27367      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27368      * @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)
27369      * @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)
27370      * @return {Roo.UpdateManager} The UpdateManager
27371      */
27372     setUrl : function(url, params, loadOnce){
27373         if(this.refreshDelegate){
27374             this.un('activate', this.refreshDelegate);
27375         }
27376         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27377         this.on("activate", this.refreshDelegate);
27378         return this.bodyEl.getUpdateManager();
27379     },
27380
27381     /** @private */
27382     _handleRefresh : function(url, params, loadOnce){
27383         if(!loadOnce || !this.loaded){
27384             var updater = this.bodyEl.getUpdateManager();
27385             updater.update(url, params, this._setLoaded.createDelegate(this));
27386         }
27387     },
27388
27389     /**
27390      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27391      *   Will fail silently if the setUrl method has not been called.
27392      *   This does not activate the panel, just updates its content.
27393      */
27394     refresh : function(){
27395         if(this.refreshDelegate){
27396            this.loaded = false;
27397            this.refreshDelegate();
27398         }
27399     },
27400
27401     /** @private */
27402     _setLoaded : function(){
27403         this.loaded = true;
27404     },
27405
27406     /** @private */
27407     closeClick : function(e){
27408         var o = {};
27409         e.stopEvent();
27410         this.fireEvent("beforeclose", this, o);
27411         if(o.cancel !== true){
27412             this.tabPanel.removeTab(this.id);
27413         }
27414     },
27415     /**
27416      * The text displayed in the tooltip for the close icon.
27417      * @type String
27418      */
27419     closeText : "Close this tab"
27420 });
27421
27422 /** @private */
27423 Roo.TabPanel.prototype.createStrip = function(container){
27424     var strip = document.createElement("div");
27425     strip.className = "x-tabs-wrap";
27426     container.appendChild(strip);
27427     return strip;
27428 };
27429 /** @private */
27430 Roo.TabPanel.prototype.createStripList = function(strip){
27431     // div wrapper for retard IE
27432     // returns the "tr" element.
27433     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27434         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27435         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27436     return strip.firstChild.firstChild.firstChild.firstChild;
27437 };
27438 /** @private */
27439 Roo.TabPanel.prototype.createBody = function(container){
27440     var body = document.createElement("div");
27441     Roo.id(body, "tab-body");
27442     Roo.fly(body).addClass("x-tabs-body");
27443     container.appendChild(body);
27444     return body;
27445 };
27446 /** @private */
27447 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27448     var body = Roo.getDom(id);
27449     if(!body){
27450         body = document.createElement("div");
27451         body.id = id;
27452     }
27453     Roo.fly(body).addClass("x-tabs-item-body");
27454     bodyEl.insertBefore(body, bodyEl.firstChild);
27455     return body;
27456 };
27457 /** @private */
27458 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27459     var td = document.createElement("td");
27460     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27461     //stripEl.appendChild(td);
27462     if(closable){
27463         td.className = "x-tabs-closable";
27464         if(!this.closeTpl){
27465             this.closeTpl = new Roo.Template(
27466                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27467                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27468                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27469             );
27470         }
27471         var el = this.closeTpl.overwrite(td, {"text": text});
27472         var close = el.getElementsByTagName("div")[0];
27473         var inner = el.getElementsByTagName("em")[0];
27474         return {"el": el, "close": close, "inner": inner};
27475     } else {
27476         if(!this.tabTpl){
27477             this.tabTpl = new Roo.Template(
27478                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27479                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27480             );
27481         }
27482         var el = this.tabTpl.overwrite(td, {"text": text});
27483         var inner = el.getElementsByTagName("em")[0];
27484         return {"el": el, "inner": inner};
27485     }
27486 };/*
27487  * Based on:
27488  * Ext JS Library 1.1.1
27489  * Copyright(c) 2006-2007, Ext JS, LLC.
27490  *
27491  * Originally Released Under LGPL - original licence link has changed is not relivant.
27492  *
27493  * Fork - LGPL
27494  * <script type="text/javascript">
27495  */
27496
27497 /**
27498  * @class Roo.Button
27499  * @extends Roo.util.Observable
27500  * Simple Button class
27501  * @cfg {String} text The button text
27502  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27503  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27504  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27505  * @cfg {Object} scope The scope of the handler
27506  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27507  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27508  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27509  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27510  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27511  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27512    applies if enableToggle = true)
27513  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27514  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27515   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27516  * @constructor
27517  * Create a new button
27518  * @param {Object} config The config object
27519  */
27520 Roo.Button = function(renderTo, config)
27521 {
27522     if (!config) {
27523         config = renderTo;
27524         renderTo = config.renderTo || false;
27525     }
27526     
27527     Roo.apply(this, config);
27528     this.addEvents({
27529         /**
27530              * @event click
27531              * Fires when this button is clicked
27532              * @param {Button} this
27533              * @param {EventObject} e The click event
27534              */
27535             "click" : true,
27536         /**
27537              * @event toggle
27538              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27539              * @param {Button} this
27540              * @param {Boolean} pressed
27541              */
27542             "toggle" : true,
27543         /**
27544              * @event mouseover
27545              * Fires when the mouse hovers over the button
27546              * @param {Button} this
27547              * @param {Event} e The event object
27548              */
27549         'mouseover' : true,
27550         /**
27551              * @event mouseout
27552              * Fires when the mouse exits the button
27553              * @param {Button} this
27554              * @param {Event} e The event object
27555              */
27556         'mouseout': true,
27557          /**
27558              * @event render
27559              * Fires when the button is rendered
27560              * @param {Button} this
27561              */
27562         'render': true
27563     });
27564     if(this.menu){
27565         this.menu = Roo.menu.MenuMgr.get(this.menu);
27566     }
27567     // register listeners first!!  - so render can be captured..
27568     Roo.util.Observable.call(this);
27569     if(renderTo){
27570         this.render(renderTo);
27571     }
27572     
27573   
27574 };
27575
27576 Roo.extend(Roo.Button, Roo.util.Observable, {
27577     /**
27578      * 
27579      */
27580     
27581     /**
27582      * Read-only. True if this button is hidden
27583      * @type Boolean
27584      */
27585     hidden : false,
27586     /**
27587      * Read-only. True if this button is disabled
27588      * @type Boolean
27589      */
27590     disabled : false,
27591     /**
27592      * Read-only. True if this button is pressed (only if enableToggle = true)
27593      * @type Boolean
27594      */
27595     pressed : false,
27596
27597     /**
27598      * @cfg {Number} tabIndex 
27599      * The DOM tabIndex for this button (defaults to undefined)
27600      */
27601     tabIndex : undefined,
27602
27603     /**
27604      * @cfg {Boolean} enableToggle
27605      * True to enable pressed/not pressed toggling (defaults to false)
27606      */
27607     enableToggle: false,
27608     /**
27609      * @cfg {Mixed} menu
27610      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27611      */
27612     menu : undefined,
27613     /**
27614      * @cfg {String} menuAlign
27615      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27616      */
27617     menuAlign : "tl-bl?",
27618
27619     /**
27620      * @cfg {String} iconCls
27621      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27622      */
27623     iconCls : undefined,
27624     /**
27625      * @cfg {String} type
27626      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27627      */
27628     type : 'button',
27629
27630     // private
27631     menuClassTarget: 'tr',
27632
27633     /**
27634      * @cfg {String} clickEvent
27635      * The type of event to map to the button's event handler (defaults to 'click')
27636      */
27637     clickEvent : 'click',
27638
27639     /**
27640      * @cfg {Boolean} handleMouseEvents
27641      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27642      */
27643     handleMouseEvents : true,
27644
27645     /**
27646      * @cfg {String} tooltipType
27647      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27648      */
27649     tooltipType : 'qtip',
27650
27651     /**
27652      * @cfg {String} cls
27653      * A CSS class to apply to the button's main element.
27654      */
27655     
27656     /**
27657      * @cfg {Roo.Template} template (Optional)
27658      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27659      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27660      * require code modifications if required elements (e.g. a button) aren't present.
27661      */
27662
27663     // private
27664     render : function(renderTo){
27665         var btn;
27666         if(this.hideParent){
27667             this.parentEl = Roo.get(renderTo);
27668         }
27669         if(!this.dhconfig){
27670             if(!this.template){
27671                 if(!Roo.Button.buttonTemplate){
27672                     // hideous table template
27673                     Roo.Button.buttonTemplate = new Roo.Template(
27674                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27675                         '<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>',
27676                         "</tr></tbody></table>");
27677                 }
27678                 this.template = Roo.Button.buttonTemplate;
27679             }
27680             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27681             var btnEl = btn.child("button:first");
27682             btnEl.on('focus', this.onFocus, this);
27683             btnEl.on('blur', this.onBlur, this);
27684             if(this.cls){
27685                 btn.addClass(this.cls);
27686             }
27687             if(this.icon){
27688                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27689             }
27690             if(this.iconCls){
27691                 btnEl.addClass(this.iconCls);
27692                 if(!this.cls){
27693                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27694                 }
27695             }
27696             if(this.tabIndex !== undefined){
27697                 btnEl.dom.tabIndex = this.tabIndex;
27698             }
27699             if(this.tooltip){
27700                 if(typeof this.tooltip == 'object'){
27701                     Roo.QuickTips.tips(Roo.apply({
27702                           target: btnEl.id
27703                     }, this.tooltip));
27704                 } else {
27705                     btnEl.dom[this.tooltipType] = this.tooltip;
27706                 }
27707             }
27708         }else{
27709             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27710         }
27711         this.el = btn;
27712         if(this.id){
27713             this.el.dom.id = this.el.id = this.id;
27714         }
27715         if(this.menu){
27716             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27717             this.menu.on("show", this.onMenuShow, this);
27718             this.menu.on("hide", this.onMenuHide, this);
27719         }
27720         btn.addClass("x-btn");
27721         if(Roo.isIE && !Roo.isIE7){
27722             this.autoWidth.defer(1, this);
27723         }else{
27724             this.autoWidth();
27725         }
27726         if(this.handleMouseEvents){
27727             btn.on("mouseover", this.onMouseOver, this);
27728             btn.on("mouseout", this.onMouseOut, this);
27729             btn.on("mousedown", this.onMouseDown, this);
27730         }
27731         btn.on(this.clickEvent, this.onClick, this);
27732         //btn.on("mouseup", this.onMouseUp, this);
27733         if(this.hidden){
27734             this.hide();
27735         }
27736         if(this.disabled){
27737             this.disable();
27738         }
27739         Roo.ButtonToggleMgr.register(this);
27740         if(this.pressed){
27741             this.el.addClass("x-btn-pressed");
27742         }
27743         if(this.repeat){
27744             var repeater = new Roo.util.ClickRepeater(btn,
27745                 typeof this.repeat == "object" ? this.repeat : {}
27746             );
27747             repeater.on("click", this.onClick,  this);
27748         }
27749         
27750         this.fireEvent('render', this);
27751         
27752     },
27753     /**
27754      * Returns the button's underlying element
27755      * @return {Roo.Element} The element
27756      */
27757     getEl : function(){
27758         return this.el;  
27759     },
27760     
27761     /**
27762      * Destroys this Button and removes any listeners.
27763      */
27764     destroy : function(){
27765         Roo.ButtonToggleMgr.unregister(this);
27766         this.el.removeAllListeners();
27767         this.purgeListeners();
27768         this.el.remove();
27769     },
27770
27771     // private
27772     autoWidth : function(){
27773         if(this.el){
27774             this.el.setWidth("auto");
27775             if(Roo.isIE7 && Roo.isStrict){
27776                 var ib = this.el.child('button');
27777                 if(ib && ib.getWidth() > 20){
27778                     ib.clip();
27779                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27780                 }
27781             }
27782             if(this.minWidth){
27783                 if(this.hidden){
27784                     this.el.beginMeasure();
27785                 }
27786                 if(this.el.getWidth() < this.minWidth){
27787                     this.el.setWidth(this.minWidth);
27788                 }
27789                 if(this.hidden){
27790                     this.el.endMeasure();
27791                 }
27792             }
27793         }
27794     },
27795
27796     /**
27797      * Assigns this button's click handler
27798      * @param {Function} handler The function to call when the button is clicked
27799      * @param {Object} scope (optional) Scope for the function passed in
27800      */
27801     setHandler : function(handler, scope){
27802         this.handler = handler;
27803         this.scope = scope;  
27804     },
27805     
27806     /**
27807      * Sets this button's text
27808      * @param {String} text The button text
27809      */
27810     setText : function(text){
27811         this.text = text;
27812         if(this.el){
27813             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27814         }
27815         this.autoWidth();
27816     },
27817     
27818     /**
27819      * Gets the text for this button
27820      * @return {String} The button text
27821      */
27822     getText : function(){
27823         return this.text;  
27824     },
27825     
27826     /**
27827      * Show this button
27828      */
27829     show: function(){
27830         this.hidden = false;
27831         if(this.el){
27832             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27833         }
27834     },
27835     
27836     /**
27837      * Hide this button
27838      */
27839     hide: function(){
27840         this.hidden = true;
27841         if(this.el){
27842             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27843         }
27844     },
27845     
27846     /**
27847      * Convenience function for boolean show/hide
27848      * @param {Boolean} visible True to show, false to hide
27849      */
27850     setVisible: function(visible){
27851         if(visible) {
27852             this.show();
27853         }else{
27854             this.hide();
27855         }
27856     },
27857     
27858     /**
27859      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27860      * @param {Boolean} state (optional) Force a particular state
27861      */
27862     toggle : function(state){
27863         state = state === undefined ? !this.pressed : state;
27864         if(state != this.pressed){
27865             if(state){
27866                 this.el.addClass("x-btn-pressed");
27867                 this.pressed = true;
27868                 this.fireEvent("toggle", this, true);
27869             }else{
27870                 this.el.removeClass("x-btn-pressed");
27871                 this.pressed = false;
27872                 this.fireEvent("toggle", this, false);
27873             }
27874             if(this.toggleHandler){
27875                 this.toggleHandler.call(this.scope || this, this, state);
27876             }
27877         }
27878     },
27879     
27880     /**
27881      * Focus the button
27882      */
27883     focus : function(){
27884         this.el.child('button:first').focus();
27885     },
27886     
27887     /**
27888      * Disable this button
27889      */
27890     disable : function(){
27891         if(this.el){
27892             this.el.addClass("x-btn-disabled");
27893         }
27894         this.disabled = true;
27895     },
27896     
27897     /**
27898      * Enable this button
27899      */
27900     enable : function(){
27901         if(this.el){
27902             this.el.removeClass("x-btn-disabled");
27903         }
27904         this.disabled = false;
27905     },
27906
27907     /**
27908      * Convenience function for boolean enable/disable
27909      * @param {Boolean} enabled True to enable, false to disable
27910      */
27911     setDisabled : function(v){
27912         this[v !== true ? "enable" : "disable"]();
27913     },
27914
27915     // private
27916     onClick : function(e)
27917     {
27918         if(e){
27919             e.preventDefault();
27920         }
27921         if(e.button != 0){
27922             return;
27923         }
27924         if(!this.disabled){
27925             if(this.enableToggle){
27926                 this.toggle();
27927             }
27928             if(this.menu && !this.menu.isVisible()){
27929                 this.menu.show(this.el, this.menuAlign);
27930             }
27931             this.fireEvent("click", this, e);
27932             if(this.handler){
27933                 this.el.removeClass("x-btn-over");
27934                 this.handler.call(this.scope || this, this, e);
27935             }
27936         }
27937     },
27938     // private
27939     onMouseOver : function(e){
27940         if(!this.disabled){
27941             this.el.addClass("x-btn-over");
27942             this.fireEvent('mouseover', this, e);
27943         }
27944     },
27945     // private
27946     onMouseOut : function(e){
27947         if(!e.within(this.el,  true)){
27948             this.el.removeClass("x-btn-over");
27949             this.fireEvent('mouseout', this, e);
27950         }
27951     },
27952     // private
27953     onFocus : function(e){
27954         if(!this.disabled){
27955             this.el.addClass("x-btn-focus");
27956         }
27957     },
27958     // private
27959     onBlur : function(e){
27960         this.el.removeClass("x-btn-focus");
27961     },
27962     // private
27963     onMouseDown : function(e){
27964         if(!this.disabled && e.button == 0){
27965             this.el.addClass("x-btn-click");
27966             Roo.get(document).on('mouseup', this.onMouseUp, this);
27967         }
27968     },
27969     // private
27970     onMouseUp : function(e){
27971         if(e.button == 0){
27972             this.el.removeClass("x-btn-click");
27973             Roo.get(document).un('mouseup', this.onMouseUp, this);
27974         }
27975     },
27976     // private
27977     onMenuShow : function(e){
27978         this.el.addClass("x-btn-menu-active");
27979     },
27980     // private
27981     onMenuHide : function(e){
27982         this.el.removeClass("x-btn-menu-active");
27983     }   
27984 });
27985
27986 // Private utility class used by Button
27987 Roo.ButtonToggleMgr = function(){
27988    var groups = {};
27989    
27990    function toggleGroup(btn, state){
27991        if(state){
27992            var g = groups[btn.toggleGroup];
27993            for(var i = 0, l = g.length; i < l; i++){
27994                if(g[i] != btn){
27995                    g[i].toggle(false);
27996                }
27997            }
27998        }
27999    }
28000    
28001    return {
28002        register : function(btn){
28003            if(!btn.toggleGroup){
28004                return;
28005            }
28006            var g = groups[btn.toggleGroup];
28007            if(!g){
28008                g = groups[btn.toggleGroup] = [];
28009            }
28010            g.push(btn);
28011            btn.on("toggle", toggleGroup);
28012        },
28013        
28014        unregister : function(btn){
28015            if(!btn.toggleGroup){
28016                return;
28017            }
28018            var g = groups[btn.toggleGroup];
28019            if(g){
28020                g.remove(btn);
28021                btn.un("toggle", toggleGroup);
28022            }
28023        }
28024    };
28025 }();/*
28026  * Based on:
28027  * Ext JS Library 1.1.1
28028  * Copyright(c) 2006-2007, Ext JS, LLC.
28029  *
28030  * Originally Released Under LGPL - original licence link has changed is not relivant.
28031  *
28032  * Fork - LGPL
28033  * <script type="text/javascript">
28034  */
28035  
28036 /**
28037  * @class Roo.SplitButton
28038  * @extends Roo.Button
28039  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28040  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28041  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28042  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28043  * @cfg {String} arrowTooltip The title attribute of the arrow
28044  * @constructor
28045  * Create a new menu button
28046  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28047  * @param {Object} config The config object
28048  */
28049 Roo.SplitButton = function(renderTo, config){
28050     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28051     /**
28052      * @event arrowclick
28053      * Fires when this button's arrow is clicked
28054      * @param {SplitButton} this
28055      * @param {EventObject} e The click event
28056      */
28057     this.addEvents({"arrowclick":true});
28058 };
28059
28060 Roo.extend(Roo.SplitButton, Roo.Button, {
28061     render : function(renderTo){
28062         // this is one sweet looking template!
28063         var tpl = new Roo.Template(
28064             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28065             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28066             '<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>',
28067             "</tbody></table></td><td>",
28068             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28069             '<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>',
28070             "</tbody></table></td></tr></table>"
28071         );
28072         var btn = tpl.append(renderTo, [this.text, this.type], true);
28073         var btnEl = btn.child("button");
28074         if(this.cls){
28075             btn.addClass(this.cls);
28076         }
28077         if(this.icon){
28078             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28079         }
28080         if(this.iconCls){
28081             btnEl.addClass(this.iconCls);
28082             if(!this.cls){
28083                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28084             }
28085         }
28086         this.el = btn;
28087         if(this.handleMouseEvents){
28088             btn.on("mouseover", this.onMouseOver, this);
28089             btn.on("mouseout", this.onMouseOut, this);
28090             btn.on("mousedown", this.onMouseDown, this);
28091             btn.on("mouseup", this.onMouseUp, this);
28092         }
28093         btn.on(this.clickEvent, this.onClick, this);
28094         if(this.tooltip){
28095             if(typeof this.tooltip == 'object'){
28096                 Roo.QuickTips.tips(Roo.apply({
28097                       target: btnEl.id
28098                 }, this.tooltip));
28099             } else {
28100                 btnEl.dom[this.tooltipType] = this.tooltip;
28101             }
28102         }
28103         if(this.arrowTooltip){
28104             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28105         }
28106         if(this.hidden){
28107             this.hide();
28108         }
28109         if(this.disabled){
28110             this.disable();
28111         }
28112         if(this.pressed){
28113             this.el.addClass("x-btn-pressed");
28114         }
28115         if(Roo.isIE && !Roo.isIE7){
28116             this.autoWidth.defer(1, this);
28117         }else{
28118             this.autoWidth();
28119         }
28120         if(this.menu){
28121             this.menu.on("show", this.onMenuShow, this);
28122             this.menu.on("hide", this.onMenuHide, this);
28123         }
28124         this.fireEvent('render', this);
28125     },
28126
28127     // private
28128     autoWidth : function(){
28129         if(this.el){
28130             var tbl = this.el.child("table:first");
28131             var tbl2 = this.el.child("table:last");
28132             this.el.setWidth("auto");
28133             tbl.setWidth("auto");
28134             if(Roo.isIE7 && Roo.isStrict){
28135                 var ib = this.el.child('button:first');
28136                 if(ib && ib.getWidth() > 20){
28137                     ib.clip();
28138                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28139                 }
28140             }
28141             if(this.minWidth){
28142                 if(this.hidden){
28143                     this.el.beginMeasure();
28144                 }
28145                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28146                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28147                 }
28148                 if(this.hidden){
28149                     this.el.endMeasure();
28150                 }
28151             }
28152             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28153         } 
28154     },
28155     /**
28156      * Sets this button's click handler
28157      * @param {Function} handler The function to call when the button is clicked
28158      * @param {Object} scope (optional) Scope for the function passed above
28159      */
28160     setHandler : function(handler, scope){
28161         this.handler = handler;
28162         this.scope = scope;  
28163     },
28164     
28165     /**
28166      * Sets this button's arrow click handler
28167      * @param {Function} handler The function to call when the arrow is clicked
28168      * @param {Object} scope (optional) Scope for the function passed above
28169      */
28170     setArrowHandler : function(handler, scope){
28171         this.arrowHandler = handler;
28172         this.scope = scope;  
28173     },
28174     
28175     /**
28176      * Focus the button
28177      */
28178     focus : function(){
28179         if(this.el){
28180             this.el.child("button:first").focus();
28181         }
28182     },
28183
28184     // private
28185     onClick : function(e){
28186         e.preventDefault();
28187         if(!this.disabled){
28188             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28189                 if(this.menu && !this.menu.isVisible()){
28190                     this.menu.show(this.el, this.menuAlign);
28191                 }
28192                 this.fireEvent("arrowclick", this, e);
28193                 if(this.arrowHandler){
28194                     this.arrowHandler.call(this.scope || this, this, e);
28195                 }
28196             }else{
28197                 this.fireEvent("click", this, e);
28198                 if(this.handler){
28199                     this.handler.call(this.scope || this, this, e);
28200                 }
28201             }
28202         }
28203     },
28204     // private
28205     onMouseDown : function(e){
28206         if(!this.disabled){
28207             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28208         }
28209     },
28210     // private
28211     onMouseUp : function(e){
28212         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28213     }   
28214 });
28215
28216
28217 // backwards compat
28218 Roo.MenuButton = Roo.SplitButton;/*
28219  * Based on:
28220  * Ext JS Library 1.1.1
28221  * Copyright(c) 2006-2007, Ext JS, LLC.
28222  *
28223  * Originally Released Under LGPL - original licence link has changed is not relivant.
28224  *
28225  * Fork - LGPL
28226  * <script type="text/javascript">
28227  */
28228
28229 /**
28230  * @class Roo.Toolbar
28231  * Basic Toolbar class.
28232  * @constructor
28233  * Creates a new Toolbar
28234  * @param {Object} container The config object
28235  */ 
28236 Roo.Toolbar = function(container, buttons, config)
28237 {
28238     /// old consturctor format still supported..
28239     if(container instanceof Array){ // omit the container for later rendering
28240         buttons = container;
28241         config = buttons;
28242         container = null;
28243     }
28244     if (typeof(container) == 'object' && container.xtype) {
28245         config = container;
28246         container = config.container;
28247         buttons = config.buttons || []; // not really - use items!!
28248     }
28249     var xitems = [];
28250     if (config && config.items) {
28251         xitems = config.items;
28252         delete config.items;
28253     }
28254     Roo.apply(this, config);
28255     this.buttons = buttons;
28256     
28257     if(container){
28258         this.render(container);
28259     }
28260     this.xitems = xitems;
28261     Roo.each(xitems, function(b) {
28262         this.add(b);
28263     }, this);
28264     
28265 };
28266
28267 Roo.Toolbar.prototype = {
28268     /**
28269      * @cfg {Array} items
28270      * array of button configs or elements to add (will be converted to a MixedCollection)
28271      */
28272     
28273     /**
28274      * @cfg {String/HTMLElement/Element} container
28275      * The id or element that will contain the toolbar
28276      */
28277     // private
28278     render : function(ct){
28279         this.el = Roo.get(ct);
28280         if(this.cls){
28281             this.el.addClass(this.cls);
28282         }
28283         // using a table allows for vertical alignment
28284         // 100% width is needed by Safari...
28285         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28286         this.tr = this.el.child("tr", true);
28287         var autoId = 0;
28288         this.items = new Roo.util.MixedCollection(false, function(o){
28289             return o.id || ("item" + (++autoId));
28290         });
28291         if(this.buttons){
28292             this.add.apply(this, this.buttons);
28293             delete this.buttons;
28294         }
28295     },
28296
28297     /**
28298      * Adds element(s) to the toolbar -- this function takes a variable number of 
28299      * arguments of mixed type and adds them to the toolbar.
28300      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28301      * <ul>
28302      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28303      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28304      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28305      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28306      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28307      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28308      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28309      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28310      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28311      * </ul>
28312      * @param {Mixed} arg2
28313      * @param {Mixed} etc.
28314      */
28315     add : function(){
28316         var a = arguments, l = a.length;
28317         for(var i = 0; i < l; i++){
28318             this._add(a[i]);
28319         }
28320     },
28321     // private..
28322     _add : function(el) {
28323         
28324         if (el.xtype) {
28325             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28326         }
28327         
28328         if (el.applyTo){ // some kind of form field
28329             return this.addField(el);
28330         } 
28331         if (el.render){ // some kind of Toolbar.Item
28332             return this.addItem(el);
28333         }
28334         if (typeof el == "string"){ // string
28335             if(el == "separator" || el == "-"){
28336                 return this.addSeparator();
28337             }
28338             if (el == " "){
28339                 return this.addSpacer();
28340             }
28341             if(el == "->"){
28342                 return this.addFill();
28343             }
28344             return this.addText(el);
28345             
28346         }
28347         if(el.tagName){ // element
28348             return this.addElement(el);
28349         }
28350         if(typeof el == "object"){ // must be button config?
28351             return this.addButton(el);
28352         }
28353         // and now what?!?!
28354         return false;
28355         
28356     },
28357     
28358     /**
28359      * Add an Xtype element
28360      * @param {Object} xtype Xtype Object
28361      * @return {Object} created Object
28362      */
28363     addxtype : function(e){
28364         return this.add(e);  
28365     },
28366     
28367     /**
28368      * Returns the Element for this toolbar.
28369      * @return {Roo.Element}
28370      */
28371     getEl : function(){
28372         return this.el;  
28373     },
28374     
28375     /**
28376      * Adds a separator
28377      * @return {Roo.Toolbar.Item} The separator item
28378      */
28379     addSeparator : function(){
28380         return this.addItem(new Roo.Toolbar.Separator());
28381     },
28382
28383     /**
28384      * Adds a spacer element
28385      * @return {Roo.Toolbar.Spacer} The spacer item
28386      */
28387     addSpacer : function(){
28388         return this.addItem(new Roo.Toolbar.Spacer());
28389     },
28390
28391     /**
28392      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28393      * @return {Roo.Toolbar.Fill} The fill item
28394      */
28395     addFill : function(){
28396         return this.addItem(new Roo.Toolbar.Fill());
28397     },
28398
28399     /**
28400      * Adds any standard HTML element to the toolbar
28401      * @param {String/HTMLElement/Element} el The element or id of the element to add
28402      * @return {Roo.Toolbar.Item} The element's item
28403      */
28404     addElement : function(el){
28405         return this.addItem(new Roo.Toolbar.Item(el));
28406     },
28407     /**
28408      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28409      * @type Roo.util.MixedCollection  
28410      */
28411     items : false,
28412      
28413     /**
28414      * Adds any Toolbar.Item or subclass
28415      * @param {Roo.Toolbar.Item} item
28416      * @return {Roo.Toolbar.Item} The item
28417      */
28418     addItem : function(item){
28419         var td = this.nextBlock();
28420         item.render(td);
28421         this.items.add(item);
28422         return item;
28423     },
28424     
28425     /**
28426      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28427      * @param {Object/Array} config A button config or array of configs
28428      * @return {Roo.Toolbar.Button/Array}
28429      */
28430     addButton : function(config){
28431         if(config instanceof Array){
28432             var buttons = [];
28433             for(var i = 0, len = config.length; i < len; i++) {
28434                 buttons.push(this.addButton(config[i]));
28435             }
28436             return buttons;
28437         }
28438         var b = config;
28439         if(!(config instanceof Roo.Toolbar.Button)){
28440             b = config.split ?
28441                 new Roo.Toolbar.SplitButton(config) :
28442                 new Roo.Toolbar.Button(config);
28443         }
28444         var td = this.nextBlock();
28445         b.render(td);
28446         this.items.add(b);
28447         return b;
28448     },
28449     
28450     /**
28451      * Adds text to the toolbar
28452      * @param {String} text The text to add
28453      * @return {Roo.Toolbar.Item} The element's item
28454      */
28455     addText : function(text){
28456         return this.addItem(new Roo.Toolbar.TextItem(text));
28457     },
28458     
28459     /**
28460      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28461      * @param {Number} index The index where the item is to be inserted
28462      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28463      * @return {Roo.Toolbar.Button/Item}
28464      */
28465     insertButton : function(index, item){
28466         if(item instanceof Array){
28467             var buttons = [];
28468             for(var i = 0, len = item.length; i < len; i++) {
28469                buttons.push(this.insertButton(index + i, item[i]));
28470             }
28471             return buttons;
28472         }
28473         if (!(item instanceof Roo.Toolbar.Button)){
28474            item = new Roo.Toolbar.Button(item);
28475         }
28476         var td = document.createElement("td");
28477         this.tr.insertBefore(td, this.tr.childNodes[index]);
28478         item.render(td);
28479         this.items.insert(index, item);
28480         return item;
28481     },
28482     
28483     /**
28484      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28485      * @param {Object} config
28486      * @return {Roo.Toolbar.Item} The element's item
28487      */
28488     addDom : function(config, returnEl){
28489         var td = this.nextBlock();
28490         Roo.DomHelper.overwrite(td, config);
28491         var ti = new Roo.Toolbar.Item(td.firstChild);
28492         ti.render(td);
28493         this.items.add(ti);
28494         return ti;
28495     },
28496
28497     /**
28498      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28499      * @type Roo.util.MixedCollection  
28500      */
28501     fields : false,
28502     
28503     /**
28504      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28505      * Note: the field should not have been rendered yet. For a field that has already been
28506      * rendered, use {@link #addElement}.
28507      * @param {Roo.form.Field} field
28508      * @return {Roo.ToolbarItem}
28509      */
28510      
28511       
28512     addField : function(field) {
28513         if (!this.fields) {
28514             var autoId = 0;
28515             this.fields = new Roo.util.MixedCollection(false, function(o){
28516                 return o.id || ("item" + (++autoId));
28517             });
28518
28519         }
28520         
28521         var td = this.nextBlock();
28522         field.render(td);
28523         var ti = new Roo.Toolbar.Item(td.firstChild);
28524         ti.render(td);
28525         this.items.add(ti);
28526         this.fields.add(field);
28527         return ti;
28528     },
28529     /**
28530      * Hide the toolbar
28531      * @method hide
28532      */
28533      
28534       
28535     hide : function()
28536     {
28537         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28538         this.el.child('div').hide();
28539     },
28540     /**
28541      * Show the toolbar
28542      * @method show
28543      */
28544     show : function()
28545     {
28546         this.el.child('div').show();
28547     },
28548       
28549     // private
28550     nextBlock : function(){
28551         var td = document.createElement("td");
28552         this.tr.appendChild(td);
28553         return td;
28554     },
28555
28556     // private
28557     destroy : function(){
28558         if(this.items){ // rendered?
28559             Roo.destroy.apply(Roo, this.items.items);
28560         }
28561         if(this.fields){ // rendered?
28562             Roo.destroy.apply(Roo, this.fields.items);
28563         }
28564         Roo.Element.uncache(this.el, this.tr);
28565     }
28566 };
28567
28568 /**
28569  * @class Roo.Toolbar.Item
28570  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28571  * @constructor
28572  * Creates a new Item
28573  * @param {HTMLElement} el 
28574  */
28575 Roo.Toolbar.Item = function(el){
28576     var cfg = {};
28577     if (typeof (el.xtype) != 'undefined') {
28578         cfg = el;
28579         el = cfg.el;
28580     }
28581     
28582     this.el = Roo.getDom(el);
28583     this.id = Roo.id(this.el);
28584     this.hidden = false;
28585     
28586     this.addEvents({
28587          /**
28588              * @event render
28589              * Fires when the button is rendered
28590              * @param {Button} this
28591              */
28592         'render': true
28593     });
28594     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28595 };
28596 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28597 //Roo.Toolbar.Item.prototype = {
28598     
28599     /**
28600      * Get this item's HTML Element
28601      * @return {HTMLElement}
28602      */
28603     getEl : function(){
28604        return this.el;  
28605     },
28606
28607     // private
28608     render : function(td){
28609         
28610          this.td = td;
28611         td.appendChild(this.el);
28612         
28613         this.fireEvent('render', this);
28614     },
28615     
28616     /**
28617      * Removes and destroys this item.
28618      */
28619     destroy : function(){
28620         this.td.parentNode.removeChild(this.td);
28621     },
28622     
28623     /**
28624      * Shows this item.
28625      */
28626     show: function(){
28627         this.hidden = false;
28628         this.td.style.display = "";
28629     },
28630     
28631     /**
28632      * Hides this item.
28633      */
28634     hide: function(){
28635         this.hidden = true;
28636         this.td.style.display = "none";
28637     },
28638     
28639     /**
28640      * Convenience function for boolean show/hide.
28641      * @param {Boolean} visible true to show/false to hide
28642      */
28643     setVisible: function(visible){
28644         if(visible) {
28645             this.show();
28646         }else{
28647             this.hide();
28648         }
28649     },
28650     
28651     /**
28652      * Try to focus this item.
28653      */
28654     focus : function(){
28655         Roo.fly(this.el).focus();
28656     },
28657     
28658     /**
28659      * Disables this item.
28660      */
28661     disable : function(){
28662         Roo.fly(this.td).addClass("x-item-disabled");
28663         this.disabled = true;
28664         this.el.disabled = true;
28665     },
28666     
28667     /**
28668      * Enables this item.
28669      */
28670     enable : function(){
28671         Roo.fly(this.td).removeClass("x-item-disabled");
28672         this.disabled = false;
28673         this.el.disabled = false;
28674     }
28675 });
28676
28677
28678 /**
28679  * @class Roo.Toolbar.Separator
28680  * @extends Roo.Toolbar.Item
28681  * A simple toolbar separator class
28682  * @constructor
28683  * Creates a new Separator
28684  */
28685 Roo.Toolbar.Separator = function(cfg){
28686     
28687     var s = document.createElement("span");
28688     s.className = "ytb-sep";
28689     if (cfg) {
28690         cfg.el = s;
28691     }
28692     
28693     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28694 };
28695 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28696     enable:Roo.emptyFn,
28697     disable:Roo.emptyFn,
28698     focus:Roo.emptyFn
28699 });
28700
28701 /**
28702  * @class Roo.Toolbar.Spacer
28703  * @extends Roo.Toolbar.Item
28704  * A simple element that adds extra horizontal space to a toolbar.
28705  * @constructor
28706  * Creates a new Spacer
28707  */
28708 Roo.Toolbar.Spacer = function(cfg){
28709     var s = document.createElement("div");
28710     s.className = "ytb-spacer";
28711     if (cfg) {
28712         cfg.el = s;
28713     }
28714     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28715 };
28716 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28717     enable:Roo.emptyFn,
28718     disable:Roo.emptyFn,
28719     focus:Roo.emptyFn
28720 });
28721
28722 /**
28723  * @class Roo.Toolbar.Fill
28724  * @extends Roo.Toolbar.Spacer
28725  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28726  * @constructor
28727  * Creates a new Spacer
28728  */
28729 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28730     // private
28731     render : function(td){
28732         td.style.width = '100%';
28733         Roo.Toolbar.Fill.superclass.render.call(this, td);
28734     }
28735 });
28736
28737 /**
28738  * @class Roo.Toolbar.TextItem
28739  * @extends Roo.Toolbar.Item
28740  * A simple class that renders text directly into a toolbar.
28741  * @constructor
28742  * Creates a new TextItem
28743  * @param {String} text
28744  */
28745 Roo.Toolbar.TextItem = function(cfg){
28746     var  text = cfg || "";
28747     if (typeof(cfg) == 'object') {
28748         text = cfg.text || "";
28749     }  else {
28750         cfg = null;
28751     }
28752     var s = document.createElement("span");
28753     s.className = "ytb-text";
28754     s.innerHTML = text;
28755     if (cfg) {
28756         cfg.el  = s;
28757     }
28758     
28759     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28760 };
28761 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28762     
28763      
28764     enable:Roo.emptyFn,
28765     disable:Roo.emptyFn,
28766     focus:Roo.emptyFn
28767 });
28768
28769 /**
28770  * @class Roo.Toolbar.Button
28771  * @extends Roo.Button
28772  * A button that renders into a toolbar.
28773  * @constructor
28774  * Creates a new Button
28775  * @param {Object} config A standard {@link Roo.Button} config object
28776  */
28777 Roo.Toolbar.Button = function(config){
28778     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28779 };
28780 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28781     render : function(td){
28782         this.td = td;
28783         Roo.Toolbar.Button.superclass.render.call(this, td);
28784     },
28785     
28786     /**
28787      * Removes and destroys this button
28788      */
28789     destroy : function(){
28790         Roo.Toolbar.Button.superclass.destroy.call(this);
28791         this.td.parentNode.removeChild(this.td);
28792     },
28793     
28794     /**
28795      * Shows this button
28796      */
28797     show: function(){
28798         this.hidden = false;
28799         this.td.style.display = "";
28800     },
28801     
28802     /**
28803      * Hides this button
28804      */
28805     hide: function(){
28806         this.hidden = true;
28807         this.td.style.display = "none";
28808     },
28809
28810     /**
28811      * Disables this item
28812      */
28813     disable : function(){
28814         Roo.fly(this.td).addClass("x-item-disabled");
28815         this.disabled = true;
28816     },
28817
28818     /**
28819      * Enables this item
28820      */
28821     enable : function(){
28822         Roo.fly(this.td).removeClass("x-item-disabled");
28823         this.disabled = false;
28824     }
28825 });
28826 // backwards compat
28827 Roo.ToolbarButton = Roo.Toolbar.Button;
28828
28829 /**
28830  * @class Roo.Toolbar.SplitButton
28831  * @extends Roo.SplitButton
28832  * A menu button that renders into a toolbar.
28833  * @constructor
28834  * Creates a new SplitButton
28835  * @param {Object} config A standard {@link Roo.SplitButton} config object
28836  */
28837 Roo.Toolbar.SplitButton = function(config){
28838     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28839 };
28840 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28841     render : function(td){
28842         this.td = td;
28843         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28844     },
28845     
28846     /**
28847      * Removes and destroys this button
28848      */
28849     destroy : function(){
28850         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28851         this.td.parentNode.removeChild(this.td);
28852     },
28853     
28854     /**
28855      * Shows this button
28856      */
28857     show: function(){
28858         this.hidden = false;
28859         this.td.style.display = "";
28860     },
28861     
28862     /**
28863      * Hides this button
28864      */
28865     hide: function(){
28866         this.hidden = true;
28867         this.td.style.display = "none";
28868     }
28869 });
28870
28871 // backwards compat
28872 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28873  * Based on:
28874  * Ext JS Library 1.1.1
28875  * Copyright(c) 2006-2007, Ext JS, LLC.
28876  *
28877  * Originally Released Under LGPL - original licence link has changed is not relivant.
28878  *
28879  * Fork - LGPL
28880  * <script type="text/javascript">
28881  */
28882  
28883 /**
28884  * @class Roo.PagingToolbar
28885  * @extends Roo.Toolbar
28886  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28887  * @constructor
28888  * Create a new PagingToolbar
28889  * @param {Object} config The config object
28890  */
28891 Roo.PagingToolbar = function(el, ds, config)
28892 {
28893     // old args format still supported... - xtype is prefered..
28894     if (typeof(el) == 'object' && el.xtype) {
28895         // created from xtype...
28896         config = el;
28897         ds = el.dataSource;
28898         el = config.container;
28899     }
28900     var items = [];
28901     if (config.items) {
28902         items = config.items;
28903         config.items = [];
28904     }
28905     
28906     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28907     this.ds = ds;
28908     this.cursor = 0;
28909     this.renderButtons(this.el);
28910     this.bind(ds);
28911     
28912     // supprot items array.
28913    
28914     Roo.each(items, function(e) {
28915         this.add(Roo.factory(e));
28916     },this);
28917     
28918 };
28919
28920 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28921     /**
28922      * @cfg {Roo.data.Store} dataSource
28923      * The underlying data store providing the paged data
28924      */
28925     /**
28926      * @cfg {String/HTMLElement/Element} container
28927      * container The id or element that will contain the toolbar
28928      */
28929     /**
28930      * @cfg {Boolean} displayInfo
28931      * True to display the displayMsg (defaults to false)
28932      */
28933     /**
28934      * @cfg {Number} pageSize
28935      * The number of records to display per page (defaults to 20)
28936      */
28937     pageSize: 20,
28938     /**
28939      * @cfg {String} displayMsg
28940      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28941      */
28942     displayMsg : 'Displaying {0} - {1} of {2}',
28943     /**
28944      * @cfg {String} emptyMsg
28945      * The message to display when no records are found (defaults to "No data to display")
28946      */
28947     emptyMsg : 'No data to display',
28948     /**
28949      * Customizable piece of the default paging text (defaults to "Page")
28950      * @type String
28951      */
28952     beforePageText : "Page",
28953     /**
28954      * Customizable piece of the default paging text (defaults to "of %0")
28955      * @type String
28956      */
28957     afterPageText : "of {0}",
28958     /**
28959      * Customizable piece of the default paging text (defaults to "First Page")
28960      * @type String
28961      */
28962     firstText : "First Page",
28963     /**
28964      * Customizable piece of the default paging text (defaults to "Previous Page")
28965      * @type String
28966      */
28967     prevText : "Previous Page",
28968     /**
28969      * Customizable piece of the default paging text (defaults to "Next Page")
28970      * @type String
28971      */
28972     nextText : "Next Page",
28973     /**
28974      * Customizable piece of the default paging text (defaults to "Last Page")
28975      * @type String
28976      */
28977     lastText : "Last Page",
28978     /**
28979      * Customizable piece of the default paging text (defaults to "Refresh")
28980      * @type String
28981      */
28982     refreshText : "Refresh",
28983
28984     // private
28985     renderButtons : function(el){
28986         Roo.PagingToolbar.superclass.render.call(this, el);
28987         this.first = this.addButton({
28988             tooltip: this.firstText,
28989             cls: "x-btn-icon x-grid-page-first",
28990             disabled: true,
28991             handler: this.onClick.createDelegate(this, ["first"])
28992         });
28993         this.prev = this.addButton({
28994             tooltip: this.prevText,
28995             cls: "x-btn-icon x-grid-page-prev",
28996             disabled: true,
28997             handler: this.onClick.createDelegate(this, ["prev"])
28998         });
28999         //this.addSeparator();
29000         this.add(this.beforePageText);
29001         this.field = Roo.get(this.addDom({
29002            tag: "input",
29003            type: "text",
29004            size: "3",
29005            value: "1",
29006            cls: "x-grid-page-number"
29007         }).el);
29008         this.field.on("keydown", this.onPagingKeydown, this);
29009         this.field.on("focus", function(){this.dom.select();});
29010         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
29011         this.field.setHeight(18);
29012         //this.addSeparator();
29013         this.next = this.addButton({
29014             tooltip: this.nextText,
29015             cls: "x-btn-icon x-grid-page-next",
29016             disabled: true,
29017             handler: this.onClick.createDelegate(this, ["next"])
29018         });
29019         this.last = this.addButton({
29020             tooltip: this.lastText,
29021             cls: "x-btn-icon x-grid-page-last",
29022             disabled: true,
29023             handler: this.onClick.createDelegate(this, ["last"])
29024         });
29025         //this.addSeparator();
29026         this.loading = this.addButton({
29027             tooltip: this.refreshText,
29028             cls: "x-btn-icon x-grid-loading",
29029             handler: this.onClick.createDelegate(this, ["refresh"])
29030         });
29031
29032         if(this.displayInfo){
29033             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
29034         }
29035     },
29036
29037     // private
29038     updateInfo : function(){
29039         if(this.displayEl){
29040             var count = this.ds.getCount();
29041             var msg = count == 0 ?
29042                 this.emptyMsg :
29043                 String.format(
29044                     this.displayMsg,
29045                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29046                 );
29047             this.displayEl.update(msg);
29048         }
29049     },
29050
29051     // private
29052     onLoad : function(ds, r, o){
29053        this.cursor = o.params ? o.params.start : 0;
29054        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29055
29056        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29057        this.field.dom.value = ap;
29058        this.first.setDisabled(ap == 1);
29059        this.prev.setDisabled(ap == 1);
29060        this.next.setDisabled(ap == ps);
29061        this.last.setDisabled(ap == ps);
29062        this.loading.enable();
29063        this.updateInfo();
29064     },
29065
29066     // private
29067     getPageData : function(){
29068         var total = this.ds.getTotalCount();
29069         return {
29070             total : total,
29071             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29072             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29073         };
29074     },
29075
29076     // private
29077     onLoadError : function(){
29078         this.loading.enable();
29079     },
29080
29081     // private
29082     onPagingKeydown : function(e){
29083         var k = e.getKey();
29084         var d = this.getPageData();
29085         if(k == e.RETURN){
29086             var v = this.field.dom.value, pageNum;
29087             if(!v || isNaN(pageNum = parseInt(v, 10))){
29088                 this.field.dom.value = d.activePage;
29089                 return;
29090             }
29091             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29092             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29093             e.stopEvent();
29094         }
29095         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))
29096         {
29097           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29098           this.field.dom.value = pageNum;
29099           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29100           e.stopEvent();
29101         }
29102         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29103         {
29104           var v = this.field.dom.value, pageNum; 
29105           var increment = (e.shiftKey) ? 10 : 1;
29106           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
29107             increment *= -1;
29108           }
29109           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29110             this.field.dom.value = d.activePage;
29111             return;
29112           }
29113           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29114           {
29115             this.field.dom.value = parseInt(v, 10) + increment;
29116             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29117             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29118           }
29119           e.stopEvent();
29120         }
29121     },
29122
29123     // private
29124     beforeLoad : function(){
29125         if(this.loading){
29126             this.loading.disable();
29127         }
29128     },
29129
29130     // private
29131     onClick : function(which){
29132         var ds = this.ds;
29133         switch(which){
29134             case "first":
29135                 ds.load({params:{start: 0, limit: this.pageSize}});
29136             break;
29137             case "prev":
29138                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29139             break;
29140             case "next":
29141                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29142             break;
29143             case "last":
29144                 var total = ds.getTotalCount();
29145                 var extra = total % this.pageSize;
29146                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29147                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29148             break;
29149             case "refresh":
29150                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29151             break;
29152         }
29153     },
29154
29155     /**
29156      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29157      * @param {Roo.data.Store} store The data store to unbind
29158      */
29159     unbind : function(ds){
29160         ds.un("beforeload", this.beforeLoad, this);
29161         ds.un("load", this.onLoad, this);
29162         ds.un("loadexception", this.onLoadError, this);
29163         ds.un("remove", this.updateInfo, this);
29164         ds.un("add", this.updateInfo, this);
29165         this.ds = undefined;
29166     },
29167
29168     /**
29169      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29170      * @param {Roo.data.Store} store The data store to bind
29171      */
29172     bind : function(ds){
29173         ds.on("beforeload", this.beforeLoad, this);
29174         ds.on("load", this.onLoad, this);
29175         ds.on("loadexception", this.onLoadError, this);
29176         ds.on("remove", this.updateInfo, this);
29177         ds.on("add", this.updateInfo, this);
29178         this.ds = ds;
29179     }
29180 });/*
29181  * Based on:
29182  * Ext JS Library 1.1.1
29183  * Copyright(c) 2006-2007, Ext JS, LLC.
29184  *
29185  * Originally Released Under LGPL - original licence link has changed is not relivant.
29186  *
29187  * Fork - LGPL
29188  * <script type="text/javascript">
29189  */
29190
29191 /**
29192  * @class Roo.Resizable
29193  * @extends Roo.util.Observable
29194  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29195  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29196  * 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
29197  * the element will be wrapped for you automatically.</p>
29198  * <p>Here is the list of valid resize handles:</p>
29199  * <pre>
29200 Value   Description
29201 ------  -------------------
29202  'n'     north
29203  's'     south
29204  'e'     east
29205  'w'     west
29206  'nw'    northwest
29207  'sw'    southwest
29208  'se'    southeast
29209  'ne'    northeast
29210  'hd'    horizontal drag
29211  'all'   all
29212 </pre>
29213  * <p>Here's an example showing the creation of a typical Resizable:</p>
29214  * <pre><code>
29215 var resizer = new Roo.Resizable("element-id", {
29216     handles: 'all',
29217     minWidth: 200,
29218     minHeight: 100,
29219     maxWidth: 500,
29220     maxHeight: 400,
29221     pinned: true
29222 });
29223 resizer.on("resize", myHandler);
29224 </code></pre>
29225  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29226  * resizer.east.setDisplayed(false);</p>
29227  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29228  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29229  * resize operation's new size (defaults to [0, 0])
29230  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29231  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29232  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29233  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29234  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29235  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29236  * @cfg {Number} width The width of the element in pixels (defaults to null)
29237  * @cfg {Number} height The height of the element in pixels (defaults to null)
29238  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29239  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29240  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29241  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29242  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29243  * in favor of the handles config option (defaults to false)
29244  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29245  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29246  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29247  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29248  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29249  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29250  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29251  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29252  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29253  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29254  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29255  * @constructor
29256  * Create a new resizable component
29257  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29258  * @param {Object} config configuration options
29259   */
29260 Roo.Resizable = function(el, config)
29261 {
29262     this.el = Roo.get(el);
29263
29264     if(config && config.wrap){
29265         config.resizeChild = this.el;
29266         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29267         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29268         this.el.setStyle("overflow", "hidden");
29269         this.el.setPositioning(config.resizeChild.getPositioning());
29270         config.resizeChild.clearPositioning();
29271         if(!config.width || !config.height){
29272             var csize = config.resizeChild.getSize();
29273             this.el.setSize(csize.width, csize.height);
29274         }
29275         if(config.pinned && !config.adjustments){
29276             config.adjustments = "auto";
29277         }
29278     }
29279
29280     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29281     this.proxy.unselectable();
29282     this.proxy.enableDisplayMode('block');
29283
29284     Roo.apply(this, config);
29285
29286     if(this.pinned){
29287         this.disableTrackOver = true;
29288         this.el.addClass("x-resizable-pinned");
29289     }
29290     // if the element isn't positioned, make it relative
29291     var position = this.el.getStyle("position");
29292     if(position != "absolute" && position != "fixed"){
29293         this.el.setStyle("position", "relative");
29294     }
29295     if(!this.handles){ // no handles passed, must be legacy style
29296         this.handles = 's,e,se';
29297         if(this.multiDirectional){
29298             this.handles += ',n,w';
29299         }
29300     }
29301     if(this.handles == "all"){
29302         this.handles = "n s e w ne nw se sw";
29303     }
29304     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29305     var ps = Roo.Resizable.positions;
29306     for(var i = 0, len = hs.length; i < len; i++){
29307         if(hs[i] && ps[hs[i]]){
29308             var pos = ps[hs[i]];
29309             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29310         }
29311     }
29312     // legacy
29313     this.corner = this.southeast;
29314     
29315     // updateBox = the box can move..
29316     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29317         this.updateBox = true;
29318     }
29319
29320     this.activeHandle = null;
29321
29322     if(this.resizeChild){
29323         if(typeof this.resizeChild == "boolean"){
29324             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29325         }else{
29326             this.resizeChild = Roo.get(this.resizeChild, true);
29327         }
29328     }
29329     
29330     if(this.adjustments == "auto"){
29331         var rc = this.resizeChild;
29332         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29333         if(rc && (hw || hn)){
29334             rc.position("relative");
29335             rc.setLeft(hw ? hw.el.getWidth() : 0);
29336             rc.setTop(hn ? hn.el.getHeight() : 0);
29337         }
29338         this.adjustments = [
29339             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29340             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29341         ];
29342     }
29343
29344     if(this.draggable){
29345         this.dd = this.dynamic ?
29346             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29347         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29348     }
29349
29350     // public events
29351     this.addEvents({
29352         /**
29353          * @event beforeresize
29354          * Fired before resize is allowed. Set enabled to false to cancel resize.
29355          * @param {Roo.Resizable} this
29356          * @param {Roo.EventObject} e The mousedown event
29357          */
29358         "beforeresize" : true,
29359         /**
29360          * @event resizing
29361          * Fired a resizing.
29362          * @param {Roo.Resizable} this
29363          * @param {Number} x The new x position
29364          * @param {Number} y The new y position
29365          * @param {Number} w The new w width
29366          * @param {Number} h The new h hight
29367          * @param {Roo.EventObject} e The mouseup event
29368          */
29369         "resizing" : true,
29370         /**
29371          * @event resize
29372          * Fired after a resize.
29373          * @param {Roo.Resizable} this
29374          * @param {Number} width The new width
29375          * @param {Number} height The new height
29376          * @param {Roo.EventObject} e The mouseup event
29377          */
29378         "resize" : true
29379     });
29380
29381     if(this.width !== null && this.height !== null){
29382         this.resizeTo(this.width, this.height);
29383     }else{
29384         this.updateChildSize();
29385     }
29386     if(Roo.isIE){
29387         this.el.dom.style.zoom = 1;
29388     }
29389     Roo.Resizable.superclass.constructor.call(this);
29390 };
29391
29392 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29393         resizeChild : false,
29394         adjustments : [0, 0],
29395         minWidth : 5,
29396         minHeight : 5,
29397         maxWidth : 10000,
29398         maxHeight : 10000,
29399         enabled : true,
29400         animate : false,
29401         duration : .35,
29402         dynamic : false,
29403         handles : false,
29404         multiDirectional : false,
29405         disableTrackOver : false,
29406         easing : 'easeOutStrong',
29407         widthIncrement : 0,
29408         heightIncrement : 0,
29409         pinned : false,
29410         width : null,
29411         height : null,
29412         preserveRatio : false,
29413         transparent: false,
29414         minX: 0,
29415         minY: 0,
29416         draggable: false,
29417
29418         /**
29419          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29420          */
29421         constrainTo: undefined,
29422         /**
29423          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29424          */
29425         resizeRegion: undefined,
29426
29427
29428     /**
29429      * Perform a manual resize
29430      * @param {Number} width
29431      * @param {Number} height
29432      */
29433     resizeTo : function(width, height){
29434         this.el.setSize(width, height);
29435         this.updateChildSize();
29436         this.fireEvent("resize", this, width, height, null);
29437     },
29438
29439     // private
29440     startSizing : function(e, handle){
29441         this.fireEvent("beforeresize", this, e);
29442         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29443
29444             if(!this.overlay){
29445                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29446                 this.overlay.unselectable();
29447                 this.overlay.enableDisplayMode("block");
29448                 this.overlay.on("mousemove", this.onMouseMove, this);
29449                 this.overlay.on("mouseup", this.onMouseUp, this);
29450             }
29451             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29452
29453             this.resizing = true;
29454             this.startBox = this.el.getBox();
29455             this.startPoint = e.getXY();
29456             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29457                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29458
29459             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29460             this.overlay.show();
29461
29462             if(this.constrainTo) {
29463                 var ct = Roo.get(this.constrainTo);
29464                 this.resizeRegion = ct.getRegion().adjust(
29465                     ct.getFrameWidth('t'),
29466                     ct.getFrameWidth('l'),
29467                     -ct.getFrameWidth('b'),
29468                     -ct.getFrameWidth('r')
29469                 );
29470             }
29471
29472             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29473             this.proxy.show();
29474             this.proxy.setBox(this.startBox);
29475             if(!this.dynamic){
29476                 this.proxy.setStyle('visibility', 'visible');
29477             }
29478         }
29479     },
29480
29481     // private
29482     onMouseDown : function(handle, e){
29483         if(this.enabled){
29484             e.stopEvent();
29485             this.activeHandle = handle;
29486             this.startSizing(e, handle);
29487         }
29488     },
29489
29490     // private
29491     onMouseUp : function(e){
29492         var size = this.resizeElement();
29493         this.resizing = false;
29494         this.handleOut();
29495         this.overlay.hide();
29496         this.proxy.hide();
29497         this.fireEvent("resize", this, size.width, size.height, e);
29498     },
29499
29500     // private
29501     updateChildSize : function(){
29502         
29503         if(this.resizeChild){
29504             var el = this.el;
29505             var child = this.resizeChild;
29506             var adj = this.adjustments;
29507             if(el.dom.offsetWidth){
29508                 var b = el.getSize(true);
29509                 child.setSize(b.width+adj[0], b.height+adj[1]);
29510             }
29511             // Second call here for IE
29512             // The first call enables instant resizing and
29513             // the second call corrects scroll bars if they
29514             // exist
29515             if(Roo.isIE){
29516                 setTimeout(function(){
29517                     if(el.dom.offsetWidth){
29518                         var b = el.getSize(true);
29519                         child.setSize(b.width+adj[0], b.height+adj[1]);
29520                     }
29521                 }, 10);
29522             }
29523         }
29524     },
29525
29526     // private
29527     snap : function(value, inc, min){
29528         if(!inc || !value) {
29529             return value;
29530         }
29531         var newValue = value;
29532         var m = value % inc;
29533         if(m > 0){
29534             if(m > (inc/2)){
29535                 newValue = value + (inc-m);
29536             }else{
29537                 newValue = value - m;
29538             }
29539         }
29540         return Math.max(min, newValue);
29541     },
29542
29543     // private
29544     resizeElement : function(){
29545         var box = this.proxy.getBox();
29546         if(this.updateBox){
29547             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29548         }else{
29549             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29550         }
29551         this.updateChildSize();
29552         if(!this.dynamic){
29553             this.proxy.hide();
29554         }
29555         return box;
29556     },
29557
29558     // private
29559     constrain : function(v, diff, m, mx){
29560         if(v - diff < m){
29561             diff = v - m;
29562         }else if(v - diff > mx){
29563             diff = mx - v;
29564         }
29565         return diff;
29566     },
29567
29568     // private
29569     onMouseMove : function(e){
29570         
29571         if(this.enabled){
29572             try{// try catch so if something goes wrong the user doesn't get hung
29573
29574             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29575                 return;
29576             }
29577
29578             //var curXY = this.startPoint;
29579             var curSize = this.curSize || this.startBox;
29580             var x = this.startBox.x, y = this.startBox.y;
29581             var ox = x, oy = y;
29582             var w = curSize.width, h = curSize.height;
29583             var ow = w, oh = h;
29584             var mw = this.minWidth, mh = this.minHeight;
29585             var mxw = this.maxWidth, mxh = this.maxHeight;
29586             var wi = this.widthIncrement;
29587             var hi = this.heightIncrement;
29588
29589             var eventXY = e.getXY();
29590             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29591             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29592
29593             var pos = this.activeHandle.position;
29594
29595             switch(pos){
29596                 case "east":
29597                     w += diffX;
29598                     w = Math.min(Math.max(mw, w), mxw);
29599                     break;
29600              
29601                 case "south":
29602                     h += diffY;
29603                     h = Math.min(Math.max(mh, h), mxh);
29604                     break;
29605                 case "southeast":
29606                     w += diffX;
29607                     h += diffY;
29608                     w = Math.min(Math.max(mw, w), mxw);
29609                     h = Math.min(Math.max(mh, h), mxh);
29610                     break;
29611                 case "north":
29612                     diffY = this.constrain(h, diffY, mh, mxh);
29613                     y += diffY;
29614                     h -= diffY;
29615                     break;
29616                 case "hdrag":
29617                     
29618                     if (wi) {
29619                         var adiffX = Math.abs(diffX);
29620                         var sub = (adiffX % wi); // how much 
29621                         if (sub > (wi/2)) { // far enough to snap
29622                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29623                         } else {
29624                             // remove difference.. 
29625                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29626                         }
29627                     }
29628                     x += diffX;
29629                     x = Math.max(this.minX, x);
29630                     break;
29631                 case "west":
29632                     diffX = this.constrain(w, diffX, mw, mxw);
29633                     x += diffX;
29634                     w -= diffX;
29635                     break;
29636                 case "northeast":
29637                     w += diffX;
29638                     w = Math.min(Math.max(mw, w), mxw);
29639                     diffY = this.constrain(h, diffY, mh, mxh);
29640                     y += diffY;
29641                     h -= diffY;
29642                     break;
29643                 case "northwest":
29644                     diffX = this.constrain(w, diffX, mw, mxw);
29645                     diffY = this.constrain(h, diffY, mh, mxh);
29646                     y += diffY;
29647                     h -= diffY;
29648                     x += diffX;
29649                     w -= diffX;
29650                     break;
29651                case "southwest":
29652                     diffX = this.constrain(w, diffX, mw, mxw);
29653                     h += diffY;
29654                     h = Math.min(Math.max(mh, h), mxh);
29655                     x += diffX;
29656                     w -= diffX;
29657                     break;
29658             }
29659
29660             var sw = this.snap(w, wi, mw);
29661             var sh = this.snap(h, hi, mh);
29662             if(sw != w || sh != h){
29663                 switch(pos){
29664                     case "northeast":
29665                         y -= sh - h;
29666                     break;
29667                     case "north":
29668                         y -= sh - h;
29669                         break;
29670                     case "southwest":
29671                         x -= sw - w;
29672                     break;
29673                     case "west":
29674                         x -= sw - w;
29675                         break;
29676                     case "northwest":
29677                         x -= sw - w;
29678                         y -= sh - h;
29679                     break;
29680                 }
29681                 w = sw;
29682                 h = sh;
29683             }
29684
29685             if(this.preserveRatio){
29686                 switch(pos){
29687                     case "southeast":
29688                     case "east":
29689                         h = oh * (w/ow);
29690                         h = Math.min(Math.max(mh, h), mxh);
29691                         w = ow * (h/oh);
29692                        break;
29693                     case "south":
29694                         w = ow * (h/oh);
29695                         w = Math.min(Math.max(mw, w), mxw);
29696                         h = oh * (w/ow);
29697                         break;
29698                     case "northeast":
29699                         w = ow * (h/oh);
29700                         w = Math.min(Math.max(mw, w), mxw);
29701                         h = oh * (w/ow);
29702                     break;
29703                     case "north":
29704                         var tw = w;
29705                         w = ow * (h/oh);
29706                         w = Math.min(Math.max(mw, w), mxw);
29707                         h = oh * (w/ow);
29708                         x += (tw - w) / 2;
29709                         break;
29710                     case "southwest":
29711                         h = oh * (w/ow);
29712                         h = Math.min(Math.max(mh, h), mxh);
29713                         var tw = w;
29714                         w = ow * (h/oh);
29715                         x += tw - w;
29716                         break;
29717                     case "west":
29718                         var th = h;
29719                         h = oh * (w/ow);
29720                         h = Math.min(Math.max(mh, h), mxh);
29721                         y += (th - h) / 2;
29722                         var tw = w;
29723                         w = ow * (h/oh);
29724                         x += tw - w;
29725                        break;
29726                     case "northwest":
29727                         var tw = w;
29728                         var th = h;
29729                         h = oh * (w/ow);
29730                         h = Math.min(Math.max(mh, h), mxh);
29731                         w = ow * (h/oh);
29732                         y += th - h;
29733                         x += tw - w;
29734                        break;
29735
29736                 }
29737             }
29738             if (pos == 'hdrag') {
29739                 w = ow;
29740             }
29741             this.proxy.setBounds(x, y, w, h);
29742             if(this.dynamic){
29743                 this.resizeElement();
29744             }
29745             }catch(e){}
29746         }
29747         this.fireEvent("resizing", this, x, y, w, h, e);
29748     },
29749
29750     // private
29751     handleOver : function(){
29752         if(this.enabled){
29753             this.el.addClass("x-resizable-over");
29754         }
29755     },
29756
29757     // private
29758     handleOut : function(){
29759         if(!this.resizing){
29760             this.el.removeClass("x-resizable-over");
29761         }
29762     },
29763
29764     /**
29765      * Returns the element this component is bound to.
29766      * @return {Roo.Element}
29767      */
29768     getEl : function(){
29769         return this.el;
29770     },
29771
29772     /**
29773      * Returns the resizeChild element (or null).
29774      * @return {Roo.Element}
29775      */
29776     getResizeChild : function(){
29777         return this.resizeChild;
29778     },
29779     groupHandler : function()
29780     {
29781         
29782     },
29783     /**
29784      * Destroys this resizable. If the element was wrapped and
29785      * removeEl is not true then the element remains.
29786      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29787      */
29788     destroy : function(removeEl){
29789         this.proxy.remove();
29790         if(this.overlay){
29791             this.overlay.removeAllListeners();
29792             this.overlay.remove();
29793         }
29794         var ps = Roo.Resizable.positions;
29795         for(var k in ps){
29796             if(typeof ps[k] != "function" && this[ps[k]]){
29797                 var h = this[ps[k]];
29798                 h.el.removeAllListeners();
29799                 h.el.remove();
29800             }
29801         }
29802         if(removeEl){
29803             this.el.update("");
29804             this.el.remove();
29805         }
29806     }
29807 });
29808
29809 // private
29810 // hash to map config positions to true positions
29811 Roo.Resizable.positions = {
29812     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29813     hd: "hdrag"
29814 };
29815
29816 // private
29817 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29818     if(!this.tpl){
29819         // only initialize the template if resizable is used
29820         var tpl = Roo.DomHelper.createTemplate(
29821             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29822         );
29823         tpl.compile();
29824         Roo.Resizable.Handle.prototype.tpl = tpl;
29825     }
29826     this.position = pos;
29827     this.rz = rz;
29828     // show north drag fro topdra
29829     var handlepos = pos == 'hdrag' ? 'north' : pos;
29830     
29831     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29832     if (pos == 'hdrag') {
29833         this.el.setStyle('cursor', 'pointer');
29834     }
29835     this.el.unselectable();
29836     if(transparent){
29837         this.el.setOpacity(0);
29838     }
29839     this.el.on("mousedown", this.onMouseDown, this);
29840     if(!disableTrackOver){
29841         this.el.on("mouseover", this.onMouseOver, this);
29842         this.el.on("mouseout", this.onMouseOut, this);
29843     }
29844 };
29845
29846 // private
29847 Roo.Resizable.Handle.prototype = {
29848     afterResize : function(rz){
29849         Roo.log('after?');
29850         // do nothing
29851     },
29852     // private
29853     onMouseDown : function(e){
29854         this.rz.onMouseDown(this, e);
29855     },
29856     // private
29857     onMouseOver : function(e){
29858         this.rz.handleOver(this, e);
29859     },
29860     // private
29861     onMouseOut : function(e){
29862         this.rz.handleOut(this, e);
29863     }
29864 };/*
29865  * Based on:
29866  * Ext JS Library 1.1.1
29867  * Copyright(c) 2006-2007, Ext JS, LLC.
29868  *
29869  * Originally Released Under LGPL - original licence link has changed is not relivant.
29870  *
29871  * Fork - LGPL
29872  * <script type="text/javascript">
29873  */
29874
29875 /**
29876  * @class Roo.Editor
29877  * @extends Roo.Component
29878  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29879  * @constructor
29880  * Create a new Editor
29881  * @param {Roo.form.Field} field The Field object (or descendant)
29882  * @param {Object} config The config object
29883  */
29884 Roo.Editor = function(field, config){
29885     Roo.Editor.superclass.constructor.call(this, config);
29886     this.field = field;
29887     this.addEvents({
29888         /**
29889              * @event beforestartedit
29890              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29891              * false from the handler of this event.
29892              * @param {Editor} this
29893              * @param {Roo.Element} boundEl The underlying element bound to this editor
29894              * @param {Mixed} value The field value being set
29895              */
29896         "beforestartedit" : true,
29897         /**
29898              * @event startedit
29899              * Fires when this editor is displayed
29900              * @param {Roo.Element} boundEl The underlying element bound to this editor
29901              * @param {Mixed} value The starting field value
29902              */
29903         "startedit" : true,
29904         /**
29905              * @event beforecomplete
29906              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29907              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29908              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29909              * event will not fire since no edit actually occurred.
29910              * @param {Editor} this
29911              * @param {Mixed} value The current field value
29912              * @param {Mixed} startValue The original field value
29913              */
29914         "beforecomplete" : true,
29915         /**
29916              * @event complete
29917              * Fires after editing is complete and any changed value has been written to the underlying field.
29918              * @param {Editor} this
29919              * @param {Mixed} value The current field value
29920              * @param {Mixed} startValue The original field value
29921              */
29922         "complete" : true,
29923         /**
29924          * @event specialkey
29925          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29926          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29927          * @param {Roo.form.Field} this
29928          * @param {Roo.EventObject} e The event object
29929          */
29930         "specialkey" : true
29931     });
29932 };
29933
29934 Roo.extend(Roo.Editor, Roo.Component, {
29935     /**
29936      * @cfg {Boolean/String} autosize
29937      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29938      * or "height" to adopt the height only (defaults to false)
29939      */
29940     /**
29941      * @cfg {Boolean} revertInvalid
29942      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29943      * validation fails (defaults to true)
29944      */
29945     /**
29946      * @cfg {Boolean} ignoreNoChange
29947      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29948      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29949      * will never be ignored.
29950      */
29951     /**
29952      * @cfg {Boolean} hideEl
29953      * False to keep the bound element visible while the editor is displayed (defaults to true)
29954      */
29955     /**
29956      * @cfg {Mixed} value
29957      * The data value of the underlying field (defaults to "")
29958      */
29959     value : "",
29960     /**
29961      * @cfg {String} alignment
29962      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29963      */
29964     alignment: "c-c?",
29965     /**
29966      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29967      * for bottom-right shadow (defaults to "frame")
29968      */
29969     shadow : "frame",
29970     /**
29971      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29972      */
29973     constrain : false,
29974     /**
29975      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29976      */
29977     completeOnEnter : false,
29978     /**
29979      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29980      */
29981     cancelOnEsc : false,
29982     /**
29983      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29984      */
29985     updateEl : false,
29986
29987     // private
29988     onRender : function(ct, position){
29989         this.el = new Roo.Layer({
29990             shadow: this.shadow,
29991             cls: "x-editor",
29992             parentEl : ct,
29993             shim : this.shim,
29994             shadowOffset:4,
29995             id: this.id,
29996             constrain: this.constrain
29997         });
29998         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29999         if(this.field.msgTarget != 'title'){
30000             this.field.msgTarget = 'qtip';
30001         }
30002         this.field.render(this.el);
30003         if(Roo.isGecko){
30004             this.field.el.dom.setAttribute('autocomplete', 'off');
30005         }
30006         this.field.on("specialkey", this.onSpecialKey, this);
30007         if(this.swallowKeys){
30008             this.field.el.swallowEvent(['keydown','keypress']);
30009         }
30010         this.field.show();
30011         this.field.on("blur", this.onBlur, this);
30012         if(this.field.grow){
30013             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
30014         }
30015     },
30016
30017     onSpecialKey : function(field, e)
30018     {
30019         //Roo.log('editor onSpecialKey');
30020         if(this.completeOnEnter && e.getKey() == e.ENTER){
30021             e.stopEvent();
30022             this.completeEdit();
30023             return;
30024         }
30025         // do not fire special key otherwise it might hide close the editor...
30026         if(e.getKey() == e.ENTER){    
30027             return;
30028         }
30029         if(this.cancelOnEsc && e.getKey() == e.ESC){
30030             this.cancelEdit();
30031             return;
30032         } 
30033         this.fireEvent('specialkey', field, e);
30034     
30035     },
30036
30037     /**
30038      * Starts the editing process and shows the editor.
30039      * @param {String/HTMLElement/Element} el The element to edit
30040      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30041       * to the innerHTML of el.
30042      */
30043     startEdit : function(el, value){
30044         if(this.editing){
30045             this.completeEdit();
30046         }
30047         this.boundEl = Roo.get(el);
30048         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30049         if(!this.rendered){
30050             this.render(this.parentEl || document.body);
30051         }
30052         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30053             return;
30054         }
30055         this.startValue = v;
30056         this.field.setValue(v);
30057         if(this.autoSize){
30058             var sz = this.boundEl.getSize();
30059             switch(this.autoSize){
30060                 case "width":
30061                 this.setSize(sz.width,  "");
30062                 break;
30063                 case "height":
30064                 this.setSize("",  sz.height);
30065                 break;
30066                 default:
30067                 this.setSize(sz.width,  sz.height);
30068             }
30069         }
30070         this.el.alignTo(this.boundEl, this.alignment);
30071         this.editing = true;
30072         if(Roo.QuickTips){
30073             Roo.QuickTips.disable();
30074         }
30075         this.show();
30076     },
30077
30078     /**
30079      * Sets the height and width of this editor.
30080      * @param {Number} width The new width
30081      * @param {Number} height The new height
30082      */
30083     setSize : function(w, h){
30084         this.field.setSize(w, h);
30085         if(this.el){
30086             this.el.sync();
30087         }
30088     },
30089
30090     /**
30091      * Realigns the editor to the bound field based on the current alignment config value.
30092      */
30093     realign : function(){
30094         this.el.alignTo(this.boundEl, this.alignment);
30095     },
30096
30097     /**
30098      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30099      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30100      */
30101     completeEdit : function(remainVisible){
30102         if(!this.editing){
30103             return;
30104         }
30105         var v = this.getValue();
30106         if(this.revertInvalid !== false && !this.field.isValid()){
30107             v = this.startValue;
30108             this.cancelEdit(true);
30109         }
30110         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30111             this.editing = false;
30112             this.hide();
30113             return;
30114         }
30115         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30116             this.editing = false;
30117             if(this.updateEl && this.boundEl){
30118                 this.boundEl.update(v);
30119             }
30120             if(remainVisible !== true){
30121                 this.hide();
30122             }
30123             this.fireEvent("complete", this, v, this.startValue);
30124         }
30125     },
30126
30127     // private
30128     onShow : function(){
30129         this.el.show();
30130         if(this.hideEl !== false){
30131             this.boundEl.hide();
30132         }
30133         this.field.show();
30134         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30135             this.fixIEFocus = true;
30136             this.deferredFocus.defer(50, this);
30137         }else{
30138             this.field.focus();
30139         }
30140         this.fireEvent("startedit", this.boundEl, this.startValue);
30141     },
30142
30143     deferredFocus : function(){
30144         if(this.editing){
30145             this.field.focus();
30146         }
30147     },
30148
30149     /**
30150      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30151      * reverted to the original starting value.
30152      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30153      * cancel (defaults to false)
30154      */
30155     cancelEdit : function(remainVisible){
30156         if(this.editing){
30157             this.setValue(this.startValue);
30158             if(remainVisible !== true){
30159                 this.hide();
30160             }
30161         }
30162     },
30163
30164     // private
30165     onBlur : function(){
30166         if(this.allowBlur !== true && this.editing){
30167             this.completeEdit();
30168         }
30169     },
30170
30171     // private
30172     onHide : function(){
30173         if(this.editing){
30174             this.completeEdit();
30175             return;
30176         }
30177         this.field.blur();
30178         if(this.field.collapse){
30179             this.field.collapse();
30180         }
30181         this.el.hide();
30182         if(this.hideEl !== false){
30183             this.boundEl.show();
30184         }
30185         if(Roo.QuickTips){
30186             Roo.QuickTips.enable();
30187         }
30188     },
30189
30190     /**
30191      * Sets the data value of the editor
30192      * @param {Mixed} value Any valid value supported by the underlying field
30193      */
30194     setValue : function(v){
30195         this.field.setValue(v);
30196     },
30197
30198     /**
30199      * Gets the data value of the editor
30200      * @return {Mixed} The data value
30201      */
30202     getValue : function(){
30203         return this.field.getValue();
30204     }
30205 });/*
30206  * Based on:
30207  * Ext JS Library 1.1.1
30208  * Copyright(c) 2006-2007, Ext JS, LLC.
30209  *
30210  * Originally Released Under LGPL - original licence link has changed is not relivant.
30211  *
30212  * Fork - LGPL
30213  * <script type="text/javascript">
30214  */
30215  
30216 /**
30217  * @class Roo.BasicDialog
30218  * @extends Roo.util.Observable
30219  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30220  * <pre><code>
30221 var dlg = new Roo.BasicDialog("my-dlg", {
30222     height: 200,
30223     width: 300,
30224     minHeight: 100,
30225     minWidth: 150,
30226     modal: true,
30227     proxyDrag: true,
30228     shadow: true
30229 });
30230 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30231 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30232 dlg.addButton('Cancel', dlg.hide, dlg);
30233 dlg.show();
30234 </code></pre>
30235   <b>A Dialog should always be a direct child of the body element.</b>
30236  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30237  * @cfg {String} title Default text to display in the title bar (defaults to null)
30238  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30239  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30240  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30241  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30242  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30243  * (defaults to null with no animation)
30244  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30245  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30246  * property for valid values (defaults to 'all')
30247  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30248  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30249  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30250  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30251  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30252  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30253  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30254  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30255  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30256  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30257  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30258  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30259  * draggable = true (defaults to false)
30260  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30261  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30262  * shadow (defaults to false)
30263  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30264  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30265  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30266  * @cfg {Array} buttons Array of buttons
30267  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30268  * @constructor
30269  * Create a new BasicDialog.
30270  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30271  * @param {Object} config Configuration options
30272  */
30273 Roo.BasicDialog = function(el, config){
30274     this.el = Roo.get(el);
30275     var dh = Roo.DomHelper;
30276     if(!this.el && config && config.autoCreate){
30277         if(typeof config.autoCreate == "object"){
30278             if(!config.autoCreate.id){
30279                 config.autoCreate.id = el;
30280             }
30281             this.el = dh.append(document.body,
30282                         config.autoCreate, true);
30283         }else{
30284             this.el = dh.append(document.body,
30285                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30286         }
30287     }
30288     el = this.el;
30289     el.setDisplayed(true);
30290     el.hide = this.hideAction;
30291     this.id = el.id;
30292     el.addClass("x-dlg");
30293
30294     Roo.apply(this, config);
30295
30296     this.proxy = el.createProxy("x-dlg-proxy");
30297     this.proxy.hide = this.hideAction;
30298     this.proxy.setOpacity(.5);
30299     this.proxy.hide();
30300
30301     if(config.width){
30302         el.setWidth(config.width);
30303     }
30304     if(config.height){
30305         el.setHeight(config.height);
30306     }
30307     this.size = el.getSize();
30308     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30309         this.xy = [config.x,config.y];
30310     }else{
30311         this.xy = el.getCenterXY(true);
30312     }
30313     /** The header element @type Roo.Element */
30314     this.header = el.child("> .x-dlg-hd");
30315     /** The body element @type Roo.Element */
30316     this.body = el.child("> .x-dlg-bd");
30317     /** The footer element @type Roo.Element */
30318     this.footer = el.child("> .x-dlg-ft");
30319
30320     if(!this.header){
30321         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30322     }
30323     if(!this.body){
30324         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30325     }
30326
30327     this.header.unselectable();
30328     if(this.title){
30329         this.header.update(this.title);
30330     }
30331     // this element allows the dialog to be focused for keyboard event
30332     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30333     this.focusEl.swallowEvent("click", true);
30334
30335     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30336
30337     // wrap the body and footer for special rendering
30338     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30339     if(this.footer){
30340         this.bwrap.dom.appendChild(this.footer.dom);
30341     }
30342
30343     this.bg = this.el.createChild({
30344         tag: "div", cls:"x-dlg-bg",
30345         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30346     });
30347     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30348
30349
30350     if(this.autoScroll !== false && !this.autoTabs){
30351         this.body.setStyle("overflow", "auto");
30352     }
30353
30354     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30355
30356     if(this.closable !== false){
30357         this.el.addClass("x-dlg-closable");
30358         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30359         this.close.on("click", this.closeClick, this);
30360         this.close.addClassOnOver("x-dlg-close-over");
30361     }
30362     if(this.collapsible !== false){
30363         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30364         this.collapseBtn.on("click", this.collapseClick, this);
30365         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30366         this.header.on("dblclick", this.collapseClick, this);
30367     }
30368     if(this.resizable !== false){
30369         this.el.addClass("x-dlg-resizable");
30370         this.resizer = new Roo.Resizable(el, {
30371             minWidth: this.minWidth || 80,
30372             minHeight:this.minHeight || 80,
30373             handles: this.resizeHandles || "all",
30374             pinned: true
30375         });
30376         this.resizer.on("beforeresize", this.beforeResize, this);
30377         this.resizer.on("resize", this.onResize, this);
30378     }
30379     if(this.draggable !== false){
30380         el.addClass("x-dlg-draggable");
30381         if (!this.proxyDrag) {
30382             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30383         }
30384         else {
30385             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30386         }
30387         dd.setHandleElId(this.header.id);
30388         dd.endDrag = this.endMove.createDelegate(this);
30389         dd.startDrag = this.startMove.createDelegate(this);
30390         dd.onDrag = this.onDrag.createDelegate(this);
30391         dd.scroll = false;
30392         this.dd = dd;
30393     }
30394     if(this.modal){
30395         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30396         this.mask.enableDisplayMode("block");
30397         this.mask.hide();
30398         this.el.addClass("x-dlg-modal");
30399     }
30400     if(this.shadow){
30401         this.shadow = new Roo.Shadow({
30402             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30403             offset : this.shadowOffset
30404         });
30405     }else{
30406         this.shadowOffset = 0;
30407     }
30408     if(Roo.useShims && this.shim !== false){
30409         this.shim = this.el.createShim();
30410         this.shim.hide = this.hideAction;
30411         this.shim.hide();
30412     }else{
30413         this.shim = false;
30414     }
30415     if(this.autoTabs){
30416         this.initTabs();
30417     }
30418     if (this.buttons) { 
30419         var bts= this.buttons;
30420         this.buttons = [];
30421         Roo.each(bts, function(b) {
30422             this.addButton(b);
30423         }, this);
30424     }
30425     
30426     
30427     this.addEvents({
30428         /**
30429          * @event keydown
30430          * Fires when a key is pressed
30431          * @param {Roo.BasicDialog} this
30432          * @param {Roo.EventObject} e
30433          */
30434         "keydown" : true,
30435         /**
30436          * @event move
30437          * Fires when this dialog is moved by the user.
30438          * @param {Roo.BasicDialog} this
30439          * @param {Number} x The new page X
30440          * @param {Number} y The new page Y
30441          */
30442         "move" : true,
30443         /**
30444          * @event resize
30445          * Fires when this dialog is resized by the user.
30446          * @param {Roo.BasicDialog} this
30447          * @param {Number} width The new width
30448          * @param {Number} height The new height
30449          */
30450         "resize" : true,
30451         /**
30452          * @event beforehide
30453          * Fires before this dialog is hidden.
30454          * @param {Roo.BasicDialog} this
30455          */
30456         "beforehide" : true,
30457         /**
30458          * @event hide
30459          * Fires when this dialog is hidden.
30460          * @param {Roo.BasicDialog} this
30461          */
30462         "hide" : true,
30463         /**
30464          * @event beforeshow
30465          * Fires before this dialog is shown.
30466          * @param {Roo.BasicDialog} this
30467          */
30468         "beforeshow" : true,
30469         /**
30470          * @event show
30471          * Fires when this dialog is shown.
30472          * @param {Roo.BasicDialog} this
30473          */
30474         "show" : true
30475     });
30476     el.on("keydown", this.onKeyDown, this);
30477     el.on("mousedown", this.toFront, this);
30478     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30479     this.el.hide();
30480     Roo.DialogManager.register(this);
30481     Roo.BasicDialog.superclass.constructor.call(this);
30482 };
30483
30484 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30485     shadowOffset: Roo.isIE ? 6 : 5,
30486     minHeight: 80,
30487     minWidth: 200,
30488     minButtonWidth: 75,
30489     defaultButton: null,
30490     buttonAlign: "right",
30491     tabTag: 'div',
30492     firstShow: true,
30493
30494     /**
30495      * Sets the dialog title text
30496      * @param {String} text The title text to display
30497      * @return {Roo.BasicDialog} this
30498      */
30499     setTitle : function(text){
30500         this.header.update(text);
30501         return this;
30502     },
30503
30504     // private
30505     closeClick : function(){
30506         this.hide();
30507     },
30508
30509     // private
30510     collapseClick : function(){
30511         this[this.collapsed ? "expand" : "collapse"]();
30512     },
30513
30514     /**
30515      * Collapses the dialog to its minimized state (only the title bar is visible).
30516      * Equivalent to the user clicking the collapse dialog button.
30517      */
30518     collapse : function(){
30519         if(!this.collapsed){
30520             this.collapsed = true;
30521             this.el.addClass("x-dlg-collapsed");
30522             this.restoreHeight = this.el.getHeight();
30523             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30524         }
30525     },
30526
30527     /**
30528      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30529      * clicking the expand dialog button.
30530      */
30531     expand : function(){
30532         if(this.collapsed){
30533             this.collapsed = false;
30534             this.el.removeClass("x-dlg-collapsed");
30535             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30536         }
30537     },
30538
30539     /**
30540      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30541      * @return {Roo.TabPanel} The tabs component
30542      */
30543     initTabs : function(){
30544         var tabs = this.getTabs();
30545         while(tabs.getTab(0)){
30546             tabs.removeTab(0);
30547         }
30548         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30549             var dom = el.dom;
30550             tabs.addTab(Roo.id(dom), dom.title);
30551             dom.title = "";
30552         });
30553         tabs.activate(0);
30554         return tabs;
30555     },
30556
30557     // private
30558     beforeResize : function(){
30559         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30560     },
30561
30562     // private
30563     onResize : function(){
30564         this.refreshSize();
30565         this.syncBodyHeight();
30566         this.adjustAssets();
30567         this.focus();
30568         this.fireEvent("resize", this, this.size.width, this.size.height);
30569     },
30570
30571     // private
30572     onKeyDown : function(e){
30573         if(this.isVisible()){
30574             this.fireEvent("keydown", this, e);
30575         }
30576     },
30577
30578     /**
30579      * Resizes the dialog.
30580      * @param {Number} width
30581      * @param {Number} height
30582      * @return {Roo.BasicDialog} this
30583      */
30584     resizeTo : function(width, height){
30585         this.el.setSize(width, height);
30586         this.size = {width: width, height: height};
30587         this.syncBodyHeight();
30588         if(this.fixedcenter){
30589             this.center();
30590         }
30591         if(this.isVisible()){
30592             this.constrainXY();
30593             this.adjustAssets();
30594         }
30595         this.fireEvent("resize", this, width, height);
30596         return this;
30597     },
30598
30599
30600     /**
30601      * Resizes the dialog to fit the specified content size.
30602      * @param {Number} width
30603      * @param {Number} height
30604      * @return {Roo.BasicDialog} this
30605      */
30606     setContentSize : function(w, h){
30607         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30608         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30609         //if(!this.el.isBorderBox()){
30610             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30611             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30612         //}
30613         if(this.tabs){
30614             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30615             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30616         }
30617         this.resizeTo(w, h);
30618         return this;
30619     },
30620
30621     /**
30622      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30623      * executed in response to a particular key being pressed while the dialog is active.
30624      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30625      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30626      * @param {Function} fn The function to call
30627      * @param {Object} scope (optional) The scope of the function
30628      * @return {Roo.BasicDialog} this
30629      */
30630     addKeyListener : function(key, fn, scope){
30631         var keyCode, shift, ctrl, alt;
30632         if(typeof key == "object" && !(key instanceof Array)){
30633             keyCode = key["key"];
30634             shift = key["shift"];
30635             ctrl = key["ctrl"];
30636             alt = key["alt"];
30637         }else{
30638             keyCode = key;
30639         }
30640         var handler = function(dlg, e){
30641             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30642                 var k = e.getKey();
30643                 if(keyCode instanceof Array){
30644                     for(var i = 0, len = keyCode.length; i < len; i++){
30645                         if(keyCode[i] == k){
30646                           fn.call(scope || window, dlg, k, e);
30647                           return;
30648                         }
30649                     }
30650                 }else{
30651                     if(k == keyCode){
30652                         fn.call(scope || window, dlg, k, e);
30653                     }
30654                 }
30655             }
30656         };
30657         this.on("keydown", handler);
30658         return this;
30659     },
30660
30661     /**
30662      * Returns the TabPanel component (creates it if it doesn't exist).
30663      * Note: If you wish to simply check for the existence of tabs without creating them,
30664      * check for a null 'tabs' property.
30665      * @return {Roo.TabPanel} The tabs component
30666      */
30667     getTabs : function(){
30668         if(!this.tabs){
30669             this.el.addClass("x-dlg-auto-tabs");
30670             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30671             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30672         }
30673         return this.tabs;
30674     },
30675
30676     /**
30677      * Adds a button to the footer section of the dialog.
30678      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30679      * object or a valid Roo.DomHelper element config
30680      * @param {Function} handler The function called when the button is clicked
30681      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30682      * @return {Roo.Button} The new button
30683      */
30684     addButton : function(config, handler, scope){
30685         var dh = Roo.DomHelper;
30686         if(!this.footer){
30687             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30688         }
30689         if(!this.btnContainer){
30690             var tb = this.footer.createChild({
30691
30692                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30693                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30694             }, null, true);
30695             this.btnContainer = tb.firstChild.firstChild.firstChild;
30696         }
30697         var bconfig = {
30698             handler: handler,
30699             scope: scope,
30700             minWidth: this.minButtonWidth,
30701             hideParent:true
30702         };
30703         if(typeof config == "string"){
30704             bconfig.text = config;
30705         }else{
30706             if(config.tag){
30707                 bconfig.dhconfig = config;
30708             }else{
30709                 Roo.apply(bconfig, config);
30710             }
30711         }
30712         var fc = false;
30713         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30714             bconfig.position = Math.max(0, bconfig.position);
30715             fc = this.btnContainer.childNodes[bconfig.position];
30716         }
30717          
30718         var btn = new Roo.Button(
30719             fc ? 
30720                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30721                 : this.btnContainer.appendChild(document.createElement("td")),
30722             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30723             bconfig
30724         );
30725         this.syncBodyHeight();
30726         if(!this.buttons){
30727             /**
30728              * Array of all the buttons that have been added to this dialog via addButton
30729              * @type Array
30730              */
30731             this.buttons = [];
30732         }
30733         this.buttons.push(btn);
30734         return btn;
30735     },
30736
30737     /**
30738      * Sets the default button to be focused when the dialog is displayed.
30739      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30740      * @return {Roo.BasicDialog} this
30741      */
30742     setDefaultButton : function(btn){
30743         this.defaultButton = btn;
30744         return this;
30745     },
30746
30747     // private
30748     getHeaderFooterHeight : function(safe){
30749         var height = 0;
30750         if(this.header){
30751            height += this.header.getHeight();
30752         }
30753         if(this.footer){
30754            var fm = this.footer.getMargins();
30755             height += (this.footer.getHeight()+fm.top+fm.bottom);
30756         }
30757         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30758         height += this.centerBg.getPadding("tb");
30759         return height;
30760     },
30761
30762     // private
30763     syncBodyHeight : function()
30764     {
30765         var bd = this.body, // the text
30766             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30767             bw = this.bwrap;
30768         var height = this.size.height - this.getHeaderFooterHeight(false);
30769         bd.setHeight(height-bd.getMargins("tb"));
30770         var hh = this.header.getHeight();
30771         var h = this.size.height-hh;
30772         cb.setHeight(h);
30773         
30774         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30775         bw.setHeight(h-cb.getPadding("tb"));
30776         
30777         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30778         bd.setWidth(bw.getWidth(true));
30779         if(this.tabs){
30780             this.tabs.syncHeight();
30781             if(Roo.isIE){
30782                 this.tabs.el.repaint();
30783             }
30784         }
30785     },
30786
30787     /**
30788      * Restores the previous state of the dialog if Roo.state is configured.
30789      * @return {Roo.BasicDialog} this
30790      */
30791     restoreState : function(){
30792         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30793         if(box && box.width){
30794             this.xy = [box.x, box.y];
30795             this.resizeTo(box.width, box.height);
30796         }
30797         return this;
30798     },
30799
30800     // private
30801     beforeShow : function(){
30802         this.expand();
30803         if(this.fixedcenter){
30804             this.xy = this.el.getCenterXY(true);
30805         }
30806         if(this.modal){
30807             Roo.get(document.body).addClass("x-body-masked");
30808             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30809             this.mask.show();
30810         }
30811         this.constrainXY();
30812     },
30813
30814     // private
30815     animShow : function(){
30816         var b = Roo.get(this.animateTarget).getBox();
30817         this.proxy.setSize(b.width, b.height);
30818         this.proxy.setLocation(b.x, b.y);
30819         this.proxy.show();
30820         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30821                     true, .35, this.showEl.createDelegate(this));
30822     },
30823
30824     /**
30825      * Shows the dialog.
30826      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30827      * @return {Roo.BasicDialog} this
30828      */
30829     show : function(animateTarget){
30830         if (this.fireEvent("beforeshow", this) === false){
30831             return;
30832         }
30833         if(this.syncHeightBeforeShow){
30834             this.syncBodyHeight();
30835         }else if(this.firstShow){
30836             this.firstShow = false;
30837             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30838         }
30839         this.animateTarget = animateTarget || this.animateTarget;
30840         if(!this.el.isVisible()){
30841             this.beforeShow();
30842             if(this.animateTarget && Roo.get(this.animateTarget)){
30843                 this.animShow();
30844             }else{
30845                 this.showEl();
30846             }
30847         }
30848         return this;
30849     },
30850
30851     // private
30852     showEl : function(){
30853         this.proxy.hide();
30854         this.el.setXY(this.xy);
30855         this.el.show();
30856         this.adjustAssets(true);
30857         this.toFront();
30858         this.focus();
30859         // IE peekaboo bug - fix found by Dave Fenwick
30860         if(Roo.isIE){
30861             this.el.repaint();
30862         }
30863         this.fireEvent("show", this);
30864     },
30865
30866     /**
30867      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30868      * dialog itself will receive focus.
30869      */
30870     focus : function(){
30871         if(this.defaultButton){
30872             this.defaultButton.focus();
30873         }else{
30874             this.focusEl.focus();
30875         }
30876     },
30877
30878     // private
30879     constrainXY : function(){
30880         if(this.constraintoviewport !== false){
30881             if(!this.viewSize){
30882                 if(this.container){
30883                     var s = this.container.getSize();
30884                     this.viewSize = [s.width, s.height];
30885                 }else{
30886                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30887                 }
30888             }
30889             var s = Roo.get(this.container||document).getScroll();
30890
30891             var x = this.xy[0], y = this.xy[1];
30892             var w = this.size.width, h = this.size.height;
30893             var vw = this.viewSize[0], vh = this.viewSize[1];
30894             // only move it if it needs it
30895             var moved = false;
30896             // first validate right/bottom
30897             if(x + w > vw+s.left){
30898                 x = vw - w;
30899                 moved = true;
30900             }
30901             if(y + h > vh+s.top){
30902                 y = vh - h;
30903                 moved = true;
30904             }
30905             // then make sure top/left isn't negative
30906             if(x < s.left){
30907                 x = s.left;
30908                 moved = true;
30909             }
30910             if(y < s.top){
30911                 y = s.top;
30912                 moved = true;
30913             }
30914             if(moved){
30915                 // cache xy
30916                 this.xy = [x, y];
30917                 if(this.isVisible()){
30918                     this.el.setLocation(x, y);
30919                     this.adjustAssets();
30920                 }
30921             }
30922         }
30923     },
30924
30925     // private
30926     onDrag : function(){
30927         if(!this.proxyDrag){
30928             this.xy = this.el.getXY();
30929             this.adjustAssets();
30930         }
30931     },
30932
30933     // private
30934     adjustAssets : function(doShow){
30935         var x = this.xy[0], y = this.xy[1];
30936         var w = this.size.width, h = this.size.height;
30937         if(doShow === true){
30938             if(this.shadow){
30939                 this.shadow.show(this.el);
30940             }
30941             if(this.shim){
30942                 this.shim.show();
30943             }
30944         }
30945         if(this.shadow && this.shadow.isVisible()){
30946             this.shadow.show(this.el);
30947         }
30948         if(this.shim && this.shim.isVisible()){
30949             this.shim.setBounds(x, y, w, h);
30950         }
30951     },
30952
30953     // private
30954     adjustViewport : function(w, h){
30955         if(!w || !h){
30956             w = Roo.lib.Dom.getViewWidth();
30957             h = Roo.lib.Dom.getViewHeight();
30958         }
30959         // cache the size
30960         this.viewSize = [w, h];
30961         if(this.modal && this.mask.isVisible()){
30962             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30963             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30964         }
30965         if(this.isVisible()){
30966             this.constrainXY();
30967         }
30968     },
30969
30970     /**
30971      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30972      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30973      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30974      */
30975     destroy : function(removeEl){
30976         if(this.isVisible()){
30977             this.animateTarget = null;
30978             this.hide();
30979         }
30980         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30981         if(this.tabs){
30982             this.tabs.destroy(removeEl);
30983         }
30984         Roo.destroy(
30985              this.shim,
30986              this.proxy,
30987              this.resizer,
30988              this.close,
30989              this.mask
30990         );
30991         if(this.dd){
30992             this.dd.unreg();
30993         }
30994         if(this.buttons){
30995            for(var i = 0, len = this.buttons.length; i < len; i++){
30996                this.buttons[i].destroy();
30997            }
30998         }
30999         this.el.removeAllListeners();
31000         if(removeEl === true){
31001             this.el.update("");
31002             this.el.remove();
31003         }
31004         Roo.DialogManager.unregister(this);
31005     },
31006
31007     // private
31008     startMove : function(){
31009         if(this.proxyDrag){
31010             this.proxy.show();
31011         }
31012         if(this.constraintoviewport !== false){
31013             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
31014         }
31015     },
31016
31017     // private
31018     endMove : function(){
31019         if(!this.proxyDrag){
31020             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
31021         }else{
31022             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
31023             this.proxy.hide();
31024         }
31025         this.refreshSize();
31026         this.adjustAssets();
31027         this.focus();
31028         this.fireEvent("move", this, this.xy[0], this.xy[1]);
31029     },
31030
31031     /**
31032      * Brings this dialog to the front of any other visible dialogs
31033      * @return {Roo.BasicDialog} this
31034      */
31035     toFront : function(){
31036         Roo.DialogManager.bringToFront(this);
31037         return this;
31038     },
31039
31040     /**
31041      * Sends this dialog to the back (under) of any other visible dialogs
31042      * @return {Roo.BasicDialog} this
31043      */
31044     toBack : function(){
31045         Roo.DialogManager.sendToBack(this);
31046         return this;
31047     },
31048
31049     /**
31050      * Centers this dialog in the viewport
31051      * @return {Roo.BasicDialog} this
31052      */
31053     center : function(){
31054         var xy = this.el.getCenterXY(true);
31055         this.moveTo(xy[0], xy[1]);
31056         return this;
31057     },
31058
31059     /**
31060      * Moves the dialog's top-left corner to the specified point
31061      * @param {Number} x
31062      * @param {Number} y
31063      * @return {Roo.BasicDialog} this
31064      */
31065     moveTo : function(x, y){
31066         this.xy = [x,y];
31067         if(this.isVisible()){
31068             this.el.setXY(this.xy);
31069             this.adjustAssets();
31070         }
31071         return this;
31072     },
31073
31074     /**
31075      * Aligns the dialog to the specified element
31076      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31077      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31078      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31079      * @return {Roo.BasicDialog} this
31080      */
31081     alignTo : function(element, position, offsets){
31082         this.xy = this.el.getAlignToXY(element, position, offsets);
31083         if(this.isVisible()){
31084             this.el.setXY(this.xy);
31085             this.adjustAssets();
31086         }
31087         return this;
31088     },
31089
31090     /**
31091      * Anchors an element to another element and realigns it when the window is resized.
31092      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31093      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31094      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31095      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31096      * is a number, it is used as the buffer delay (defaults to 50ms).
31097      * @return {Roo.BasicDialog} this
31098      */
31099     anchorTo : function(el, alignment, offsets, monitorScroll){
31100         var action = function(){
31101             this.alignTo(el, alignment, offsets);
31102         };
31103         Roo.EventManager.onWindowResize(action, this);
31104         var tm = typeof monitorScroll;
31105         if(tm != 'undefined'){
31106             Roo.EventManager.on(window, 'scroll', action, this,
31107                 {buffer: tm == 'number' ? monitorScroll : 50});
31108         }
31109         action.call(this);
31110         return this;
31111     },
31112
31113     /**
31114      * Returns true if the dialog is visible
31115      * @return {Boolean}
31116      */
31117     isVisible : function(){
31118         return this.el.isVisible();
31119     },
31120
31121     // private
31122     animHide : function(callback){
31123         var b = Roo.get(this.animateTarget).getBox();
31124         this.proxy.show();
31125         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31126         this.el.hide();
31127         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31128                     this.hideEl.createDelegate(this, [callback]));
31129     },
31130
31131     /**
31132      * Hides the dialog.
31133      * @param {Function} callback (optional) Function to call when the dialog is hidden
31134      * @return {Roo.BasicDialog} this
31135      */
31136     hide : function(callback){
31137         if (this.fireEvent("beforehide", this) === false){
31138             return;
31139         }
31140         if(this.shadow){
31141             this.shadow.hide();
31142         }
31143         if(this.shim) {
31144           this.shim.hide();
31145         }
31146         // sometimes animateTarget seems to get set.. causing problems...
31147         // this just double checks..
31148         if(this.animateTarget && Roo.get(this.animateTarget)) {
31149            this.animHide(callback);
31150         }else{
31151             this.el.hide();
31152             this.hideEl(callback);
31153         }
31154         return this;
31155     },
31156
31157     // private
31158     hideEl : function(callback){
31159         this.proxy.hide();
31160         if(this.modal){
31161             this.mask.hide();
31162             Roo.get(document.body).removeClass("x-body-masked");
31163         }
31164         this.fireEvent("hide", this);
31165         if(typeof callback == "function"){
31166             callback();
31167         }
31168     },
31169
31170     // private
31171     hideAction : function(){
31172         this.setLeft("-10000px");
31173         this.setTop("-10000px");
31174         this.setStyle("visibility", "hidden");
31175     },
31176
31177     // private
31178     refreshSize : function(){
31179         this.size = this.el.getSize();
31180         this.xy = this.el.getXY();
31181         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31182     },
31183
31184     // private
31185     // z-index is managed by the DialogManager and may be overwritten at any time
31186     setZIndex : function(index){
31187         if(this.modal){
31188             this.mask.setStyle("z-index", index);
31189         }
31190         if(this.shim){
31191             this.shim.setStyle("z-index", ++index);
31192         }
31193         if(this.shadow){
31194             this.shadow.setZIndex(++index);
31195         }
31196         this.el.setStyle("z-index", ++index);
31197         if(this.proxy){
31198             this.proxy.setStyle("z-index", ++index);
31199         }
31200         if(this.resizer){
31201             this.resizer.proxy.setStyle("z-index", ++index);
31202         }
31203
31204         this.lastZIndex = index;
31205     },
31206
31207     /**
31208      * Returns the element for this dialog
31209      * @return {Roo.Element} The underlying dialog Element
31210      */
31211     getEl : function(){
31212         return this.el;
31213     }
31214 });
31215
31216 /**
31217  * @class Roo.DialogManager
31218  * Provides global access to BasicDialogs that have been created and
31219  * support for z-indexing (layering) multiple open dialogs.
31220  */
31221 Roo.DialogManager = function(){
31222     var list = {};
31223     var accessList = [];
31224     var front = null;
31225
31226     // private
31227     var sortDialogs = function(d1, d2){
31228         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31229     };
31230
31231     // private
31232     var orderDialogs = function(){
31233         accessList.sort(sortDialogs);
31234         var seed = Roo.DialogManager.zseed;
31235         for(var i = 0, len = accessList.length; i < len; i++){
31236             var dlg = accessList[i];
31237             if(dlg){
31238                 dlg.setZIndex(seed + (i*10));
31239             }
31240         }
31241     };
31242
31243     return {
31244         /**
31245          * The starting z-index for BasicDialogs (defaults to 9000)
31246          * @type Number The z-index value
31247          */
31248         zseed : 9000,
31249
31250         // private
31251         register : function(dlg){
31252             list[dlg.id] = dlg;
31253             accessList.push(dlg);
31254         },
31255
31256         // private
31257         unregister : function(dlg){
31258             delete list[dlg.id];
31259             var i=0;
31260             var len=0;
31261             if(!accessList.indexOf){
31262                 for(  i = 0, len = accessList.length; i < len; i++){
31263                     if(accessList[i] == dlg){
31264                         accessList.splice(i, 1);
31265                         return;
31266                     }
31267                 }
31268             }else{
31269                  i = accessList.indexOf(dlg);
31270                 if(i != -1){
31271                     accessList.splice(i, 1);
31272                 }
31273             }
31274         },
31275
31276         /**
31277          * Gets a registered dialog by id
31278          * @param {String/Object} id The id of the dialog or a dialog
31279          * @return {Roo.BasicDialog} this
31280          */
31281         get : function(id){
31282             return typeof id == "object" ? id : list[id];
31283         },
31284
31285         /**
31286          * Brings the specified dialog to the front
31287          * @param {String/Object} dlg The id of the dialog or a dialog
31288          * @return {Roo.BasicDialog} this
31289          */
31290         bringToFront : function(dlg){
31291             dlg = this.get(dlg);
31292             if(dlg != front){
31293                 front = dlg;
31294                 dlg._lastAccess = new Date().getTime();
31295                 orderDialogs();
31296             }
31297             return dlg;
31298         },
31299
31300         /**
31301          * Sends the specified dialog to the back
31302          * @param {String/Object} dlg The id of the dialog or a dialog
31303          * @return {Roo.BasicDialog} this
31304          */
31305         sendToBack : function(dlg){
31306             dlg = this.get(dlg);
31307             dlg._lastAccess = -(new Date().getTime());
31308             orderDialogs();
31309             return dlg;
31310         },
31311
31312         /**
31313          * Hides all dialogs
31314          */
31315         hideAll : function(){
31316             for(var id in list){
31317                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31318                     list[id].hide();
31319                 }
31320             }
31321         }
31322     };
31323 }();
31324
31325 /**
31326  * @class Roo.LayoutDialog
31327  * @extends Roo.BasicDialog
31328  * Dialog which provides adjustments for working with a layout in a Dialog.
31329  * Add your necessary layout config options to the dialog's config.<br>
31330  * Example usage (including a nested layout):
31331  * <pre><code>
31332 if(!dialog){
31333     dialog = new Roo.LayoutDialog("download-dlg", {
31334         modal: true,
31335         width:600,
31336         height:450,
31337         shadow:true,
31338         minWidth:500,
31339         minHeight:350,
31340         autoTabs:true,
31341         proxyDrag:true,
31342         // layout config merges with the dialog config
31343         center:{
31344             tabPosition: "top",
31345             alwaysShowTabs: true
31346         }
31347     });
31348     dialog.addKeyListener(27, dialog.hide, dialog);
31349     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31350     dialog.addButton("Build It!", this.getDownload, this);
31351
31352     // we can even add nested layouts
31353     var innerLayout = new Roo.BorderLayout("dl-inner", {
31354         east: {
31355             initialSize: 200,
31356             autoScroll:true,
31357             split:true
31358         },
31359         center: {
31360             autoScroll:true
31361         }
31362     });
31363     innerLayout.beginUpdate();
31364     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31365     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31366     innerLayout.endUpdate(true);
31367
31368     var layout = dialog.getLayout();
31369     layout.beginUpdate();
31370     layout.add("center", new Roo.ContentPanel("standard-panel",
31371                         {title: "Download the Source", fitToFrame:true}));
31372     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31373                {title: "Build your own roo.js"}));
31374     layout.getRegion("center").showPanel(sp);
31375     layout.endUpdate();
31376 }
31377 </code></pre>
31378     * @constructor
31379     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31380     * @param {Object} config configuration options
31381   */
31382 Roo.LayoutDialog = function(el, cfg){
31383     
31384     var config=  cfg;
31385     if (typeof(cfg) == 'undefined') {
31386         config = Roo.apply({}, el);
31387         // not sure why we use documentElement here.. - it should always be body.
31388         // IE7 borks horribly if we use documentElement.
31389         // webkit also does not like documentElement - it creates a body element...
31390         el = Roo.get( document.body || document.documentElement ).createChild();
31391         //config.autoCreate = true;
31392     }
31393     
31394     
31395     config.autoTabs = false;
31396     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31397     this.body.setStyle({overflow:"hidden", position:"relative"});
31398     this.layout = new Roo.BorderLayout(this.body.dom, config);
31399     this.layout.monitorWindowResize = false;
31400     this.el.addClass("x-dlg-auto-layout");
31401     // fix case when center region overwrites center function
31402     this.center = Roo.BasicDialog.prototype.center;
31403     this.on("show", this.layout.layout, this.layout, true);
31404     if (config.items) {
31405         var xitems = config.items;
31406         delete config.items;
31407         Roo.each(xitems, this.addxtype, this);
31408     }
31409     
31410     
31411 };
31412 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31413     /**
31414      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31415      * @deprecated
31416      */
31417     endUpdate : function(){
31418         this.layout.endUpdate();
31419     },
31420
31421     /**
31422      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31423      *  @deprecated
31424      */
31425     beginUpdate : function(){
31426         this.layout.beginUpdate();
31427     },
31428
31429     /**
31430      * Get the BorderLayout for this dialog
31431      * @return {Roo.BorderLayout}
31432      */
31433     getLayout : function(){
31434         return this.layout;
31435     },
31436
31437     showEl : function(){
31438         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31439         if(Roo.isIE7){
31440             this.layout.layout();
31441         }
31442     },
31443
31444     // private
31445     // Use the syncHeightBeforeShow config option to control this automatically
31446     syncBodyHeight : function(){
31447         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31448         if(this.layout){this.layout.layout();}
31449     },
31450     
31451       /**
31452      * Add an xtype element (actually adds to the layout.)
31453      * @return {Object} xdata xtype object data.
31454      */
31455     
31456     addxtype : function(c) {
31457         return this.layout.addxtype(c);
31458     }
31459 });/*
31460  * Based on:
31461  * Ext JS Library 1.1.1
31462  * Copyright(c) 2006-2007, Ext JS, LLC.
31463  *
31464  * Originally Released Under LGPL - original licence link has changed is not relivant.
31465  *
31466  * Fork - LGPL
31467  * <script type="text/javascript">
31468  */
31469  
31470 /**
31471  * @class Roo.MessageBox
31472  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31473  * Example usage:
31474  *<pre><code>
31475 // Basic alert:
31476 Roo.Msg.alert('Status', 'Changes saved successfully.');
31477
31478 // Prompt for user data:
31479 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31480     if (btn == 'ok'){
31481         // process text value...
31482     }
31483 });
31484
31485 // Show a dialog using config options:
31486 Roo.Msg.show({
31487    title:'Save Changes?',
31488    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31489    buttons: Roo.Msg.YESNOCANCEL,
31490    fn: processResult,
31491    animEl: 'elId'
31492 });
31493 </code></pre>
31494  * @singleton
31495  */
31496 Roo.MessageBox = function(){
31497     var dlg, opt, mask, waitTimer;
31498     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31499     var buttons, activeTextEl, bwidth;
31500
31501     // private
31502     var handleButton = function(button){
31503         dlg.hide();
31504         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31505     };
31506
31507     // private
31508     var handleHide = function(){
31509         if(opt && opt.cls){
31510             dlg.el.removeClass(opt.cls);
31511         }
31512         if(waitTimer){
31513             Roo.TaskMgr.stop(waitTimer);
31514             waitTimer = null;
31515         }
31516     };
31517
31518     // private
31519     var updateButtons = function(b){
31520         var width = 0;
31521         if(!b){
31522             buttons["ok"].hide();
31523             buttons["cancel"].hide();
31524             buttons["yes"].hide();
31525             buttons["no"].hide();
31526             dlg.footer.dom.style.display = 'none';
31527             return width;
31528         }
31529         dlg.footer.dom.style.display = '';
31530         for(var k in buttons){
31531             if(typeof buttons[k] != "function"){
31532                 if(b[k]){
31533                     buttons[k].show();
31534                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31535                     width += buttons[k].el.getWidth()+15;
31536                 }else{
31537                     buttons[k].hide();
31538                 }
31539             }
31540         }
31541         return width;
31542     };
31543
31544     // private
31545     var handleEsc = function(d, k, e){
31546         if(opt && opt.closable !== false){
31547             dlg.hide();
31548         }
31549         if(e){
31550             e.stopEvent();
31551         }
31552     };
31553
31554     return {
31555         /**
31556          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31557          * @return {Roo.BasicDialog} The BasicDialog element
31558          */
31559         getDialog : function(){
31560            if(!dlg){
31561                 dlg = new Roo.BasicDialog("x-msg-box", {
31562                     autoCreate : true,
31563                     shadow: true,
31564                     draggable: true,
31565                     resizable:false,
31566                     constraintoviewport:false,
31567                     fixedcenter:true,
31568                     collapsible : false,
31569                     shim:true,
31570                     modal: true,
31571                     width:400, height:100,
31572                     buttonAlign:"center",
31573                     closeClick : function(){
31574                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31575                             handleButton("no");
31576                         }else{
31577                             handleButton("cancel");
31578                         }
31579                     }
31580                 });
31581                 dlg.on("hide", handleHide);
31582                 mask = dlg.mask;
31583                 dlg.addKeyListener(27, handleEsc);
31584                 buttons = {};
31585                 var bt = this.buttonText;
31586                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31587                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31588                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31589                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31590                 bodyEl = dlg.body.createChild({
31591
31592                     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>'
31593                 });
31594                 msgEl = bodyEl.dom.firstChild;
31595                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31596                 textboxEl.enableDisplayMode();
31597                 textboxEl.addKeyListener([10,13], function(){
31598                     if(dlg.isVisible() && opt && opt.buttons){
31599                         if(opt.buttons.ok){
31600                             handleButton("ok");
31601                         }else if(opt.buttons.yes){
31602                             handleButton("yes");
31603                         }
31604                     }
31605                 });
31606                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31607                 textareaEl.enableDisplayMode();
31608                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31609                 progressEl.enableDisplayMode();
31610                 var pf = progressEl.dom.firstChild;
31611                 if (pf) {
31612                     pp = Roo.get(pf.firstChild);
31613                     pp.setHeight(pf.offsetHeight);
31614                 }
31615                 
31616             }
31617             return dlg;
31618         },
31619
31620         /**
31621          * Updates the message box body text
31622          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31623          * the XHTML-compliant non-breaking space character '&amp;#160;')
31624          * @return {Roo.MessageBox} This message box
31625          */
31626         updateText : function(text){
31627             if(!dlg.isVisible() && !opt.width){
31628                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31629             }
31630             msgEl.innerHTML = text || '&#160;';
31631       
31632             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31633             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31634             var w = Math.max(
31635                     Math.min(opt.width || cw , this.maxWidth), 
31636                     Math.max(opt.minWidth || this.minWidth, bwidth)
31637             );
31638             if(opt.prompt){
31639                 activeTextEl.setWidth(w);
31640             }
31641             if(dlg.isVisible()){
31642                 dlg.fixedcenter = false;
31643             }
31644             // to big, make it scroll. = But as usual stupid IE does not support
31645             // !important..
31646             
31647             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31648                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31649                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31650             } else {
31651                 bodyEl.dom.style.height = '';
31652                 bodyEl.dom.style.overflowY = '';
31653             }
31654             if (cw > w) {
31655                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31656             } else {
31657                 bodyEl.dom.style.overflowX = '';
31658             }
31659             
31660             dlg.setContentSize(w, bodyEl.getHeight());
31661             if(dlg.isVisible()){
31662                 dlg.fixedcenter = true;
31663             }
31664             return this;
31665         },
31666
31667         /**
31668          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31669          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31670          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31671          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31672          * @return {Roo.MessageBox} This message box
31673          */
31674         updateProgress : function(value, text){
31675             if(text){
31676                 this.updateText(text);
31677             }
31678             if (pp) { // weird bug on my firefox - for some reason this is not defined
31679                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31680             }
31681             return this;
31682         },        
31683
31684         /**
31685          * Returns true if the message box is currently displayed
31686          * @return {Boolean} True if the message box is visible, else false
31687          */
31688         isVisible : function(){
31689             return dlg && dlg.isVisible();  
31690         },
31691
31692         /**
31693          * Hides the message box if it is displayed
31694          */
31695         hide : function(){
31696             if(this.isVisible()){
31697                 dlg.hide();
31698             }  
31699         },
31700
31701         /**
31702          * Displays a new message box, or reinitializes an existing message box, based on the config options
31703          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31704          * The following config object properties are supported:
31705          * <pre>
31706 Property    Type             Description
31707 ----------  ---------------  ------------------------------------------------------------------------------------
31708 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31709                                    closes (defaults to undefined)
31710 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31711                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31712 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31713                                    progress and wait dialogs will ignore this property and always hide the
31714                                    close button as they can only be closed programmatically.
31715 cls               String           A custom CSS class to apply to the message box element
31716 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31717                                    displayed (defaults to 75)
31718 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31719                                    function will be btn (the name of the button that was clicked, if applicable,
31720                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31721                                    Progress and wait dialogs will ignore this option since they do not respond to
31722                                    user actions and can only be closed programmatically, so any required function
31723                                    should be called by the same code after it closes the dialog.
31724 icon              String           A CSS class that provides a background image to be used as an icon for
31725                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31726 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31727 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31728 modal             Boolean          False to allow user interaction with the page while the message box is
31729                                    displayed (defaults to true)
31730 msg               String           A string that will replace the existing message box body text (defaults
31731                                    to the XHTML-compliant non-breaking space character '&#160;')
31732 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31733 progress          Boolean          True to display a progress bar (defaults to false)
31734 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31735 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31736 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31737 title             String           The title text
31738 value             String           The string value to set into the active textbox element if displayed
31739 wait              Boolean          True to display a progress bar (defaults to false)
31740 width             Number           The width of the dialog in pixels
31741 </pre>
31742          *
31743          * Example usage:
31744          * <pre><code>
31745 Roo.Msg.show({
31746    title: 'Address',
31747    msg: 'Please enter your address:',
31748    width: 300,
31749    buttons: Roo.MessageBox.OKCANCEL,
31750    multiline: true,
31751    fn: saveAddress,
31752    animEl: 'addAddressBtn'
31753 });
31754 </code></pre>
31755          * @param {Object} config Configuration options
31756          * @return {Roo.MessageBox} This message box
31757          */
31758         show : function(options)
31759         {
31760             
31761             // this causes nightmares if you show one dialog after another
31762             // especially on callbacks..
31763              
31764             if(this.isVisible()){
31765                 
31766                 this.hide();
31767                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31768                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31769                 Roo.log("New Dialog Message:" +  options.msg )
31770                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31771                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31772                 
31773             }
31774             var d = this.getDialog();
31775             opt = options;
31776             d.setTitle(opt.title || "&#160;");
31777             d.close.setDisplayed(opt.closable !== false);
31778             activeTextEl = textboxEl;
31779             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31780             if(opt.prompt){
31781                 if(opt.multiline){
31782                     textboxEl.hide();
31783                     textareaEl.show();
31784                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31785                         opt.multiline : this.defaultTextHeight);
31786                     activeTextEl = textareaEl;
31787                 }else{
31788                     textboxEl.show();
31789                     textareaEl.hide();
31790                 }
31791             }else{
31792                 textboxEl.hide();
31793                 textareaEl.hide();
31794             }
31795             progressEl.setDisplayed(opt.progress === true);
31796             this.updateProgress(0);
31797             activeTextEl.dom.value = opt.value || "";
31798             if(opt.prompt){
31799                 dlg.setDefaultButton(activeTextEl);
31800             }else{
31801                 var bs = opt.buttons;
31802                 var db = null;
31803                 if(bs && bs.ok){
31804                     db = buttons["ok"];
31805                 }else if(bs && bs.yes){
31806                     db = buttons["yes"];
31807                 }
31808                 dlg.setDefaultButton(db);
31809             }
31810             bwidth = updateButtons(opt.buttons);
31811             this.updateText(opt.msg);
31812             if(opt.cls){
31813                 d.el.addClass(opt.cls);
31814             }
31815             d.proxyDrag = opt.proxyDrag === true;
31816             d.modal = opt.modal !== false;
31817             d.mask = opt.modal !== false ? mask : false;
31818             if(!d.isVisible()){
31819                 // force it to the end of the z-index stack so it gets a cursor in FF
31820                 document.body.appendChild(dlg.el.dom);
31821                 d.animateTarget = null;
31822                 d.show(options.animEl);
31823             }
31824             return this;
31825         },
31826
31827         /**
31828          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31829          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31830          * and closing the message box when the process is complete.
31831          * @param {String} title The title bar text
31832          * @param {String} msg The message box body text
31833          * @return {Roo.MessageBox} This message box
31834          */
31835         progress : function(title, msg){
31836             this.show({
31837                 title : title,
31838                 msg : msg,
31839                 buttons: false,
31840                 progress:true,
31841                 closable:false,
31842                 minWidth: this.minProgressWidth,
31843                 modal : true
31844             });
31845             return this;
31846         },
31847
31848         /**
31849          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31850          * If a callback function is passed it will be called after the user clicks the button, and the
31851          * id of the button that was clicked will be passed as the only parameter to the callback
31852          * (could also be the top-right close button).
31853          * @param {String} title The title bar text
31854          * @param {String} msg The message box body text
31855          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31856          * @param {Object} scope (optional) The scope of the callback function
31857          * @return {Roo.MessageBox} This message box
31858          */
31859         alert : function(title, msg, fn, scope){
31860             this.show({
31861                 title : title,
31862                 msg : msg,
31863                 buttons: this.OK,
31864                 fn: fn,
31865                 scope : scope,
31866                 modal : true
31867             });
31868             return this;
31869         },
31870
31871         /**
31872          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31873          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31874          * You are responsible for closing the message box when the process is complete.
31875          * @param {String} msg The message box body text
31876          * @param {String} title (optional) The title bar text
31877          * @return {Roo.MessageBox} This message box
31878          */
31879         wait : function(msg, title){
31880             this.show({
31881                 title : title,
31882                 msg : msg,
31883                 buttons: false,
31884                 closable:false,
31885                 progress:true,
31886                 modal:true,
31887                 width:300,
31888                 wait:true
31889             });
31890             waitTimer = Roo.TaskMgr.start({
31891                 run: function(i){
31892                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31893                 },
31894                 interval: 1000
31895             });
31896             return this;
31897         },
31898
31899         /**
31900          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31901          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31902          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31903          * @param {String} title The title bar text
31904          * @param {String} msg The message box body text
31905          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31906          * @param {Object} scope (optional) The scope of the callback function
31907          * @return {Roo.MessageBox} This message box
31908          */
31909         confirm : function(title, msg, fn, scope){
31910             this.show({
31911                 title : title,
31912                 msg : msg,
31913                 buttons: this.YESNO,
31914                 fn: fn,
31915                 scope : scope,
31916                 modal : true
31917             });
31918             return this;
31919         },
31920
31921         /**
31922          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31923          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31924          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31925          * (could also be the top-right close button) and the text that was entered will be passed as the two
31926          * parameters to the callback.
31927          * @param {String} title The title bar text
31928          * @param {String} msg The message box body text
31929          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31930          * @param {Object} scope (optional) The scope of the callback function
31931          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31932          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31933          * @return {Roo.MessageBox} This message box
31934          */
31935         prompt : function(title, msg, fn, scope, multiline){
31936             this.show({
31937                 title : title,
31938                 msg : msg,
31939                 buttons: this.OKCANCEL,
31940                 fn: fn,
31941                 minWidth:250,
31942                 scope : scope,
31943                 prompt:true,
31944                 multiline: multiline,
31945                 modal : true
31946             });
31947             return this;
31948         },
31949
31950         /**
31951          * Button config that displays a single OK button
31952          * @type Object
31953          */
31954         OK : {ok:true},
31955         /**
31956          * Button config that displays Yes and No buttons
31957          * @type Object
31958          */
31959         YESNO : {yes:true, no:true},
31960         /**
31961          * Button config that displays OK and Cancel buttons
31962          * @type Object
31963          */
31964         OKCANCEL : {ok:true, cancel:true},
31965         /**
31966          * Button config that displays Yes, No and Cancel buttons
31967          * @type Object
31968          */
31969         YESNOCANCEL : {yes:true, no:true, cancel:true},
31970
31971         /**
31972          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31973          * @type Number
31974          */
31975         defaultTextHeight : 75,
31976         /**
31977          * The maximum width in pixels of the message box (defaults to 600)
31978          * @type Number
31979          */
31980         maxWidth : 600,
31981         /**
31982          * The minimum width in pixels of the message box (defaults to 100)
31983          * @type Number
31984          */
31985         minWidth : 100,
31986         /**
31987          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31988          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31989          * @type Number
31990          */
31991         minProgressWidth : 250,
31992         /**
31993          * An object containing the default button text strings that can be overriden for localized language support.
31994          * Supported properties are: ok, cancel, yes and no.
31995          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31996          * @type Object
31997          */
31998         buttonText : {
31999             ok : "OK",
32000             cancel : "Cancel",
32001             yes : "Yes",
32002             no : "No"
32003         }
32004     };
32005 }();
32006
32007 /**
32008  * Shorthand for {@link Roo.MessageBox}
32009  */
32010 Roo.Msg = Roo.MessageBox;/*
32011  * Based on:
32012  * Ext JS Library 1.1.1
32013  * Copyright(c) 2006-2007, Ext JS, LLC.
32014  *
32015  * Originally Released Under LGPL - original licence link has changed is not relivant.
32016  *
32017  * Fork - LGPL
32018  * <script type="text/javascript">
32019  */
32020 /**
32021  * @class Roo.QuickTips
32022  * Provides attractive and customizable tooltips for any element.
32023  * @singleton
32024  */
32025 Roo.QuickTips = function(){
32026     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
32027     var ce, bd, xy, dd;
32028     var visible = false, disabled = true, inited = false;
32029     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
32030     
32031     var onOver = function(e){
32032         if(disabled){
32033             return;
32034         }
32035         var t = e.getTarget();
32036         if(!t || t.nodeType !== 1 || t == document || t == document.body){
32037             return;
32038         }
32039         if(ce && t == ce.el){
32040             clearTimeout(hideProc);
32041             return;
32042         }
32043         if(t && tagEls[t.id]){
32044             tagEls[t.id].el = t;
32045             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32046             return;
32047         }
32048         var ttp, et = Roo.fly(t);
32049         var ns = cfg.namespace;
32050         if(tm.interceptTitles && t.title){
32051             ttp = t.title;
32052             t.qtip = ttp;
32053             t.removeAttribute("title");
32054             e.preventDefault();
32055         }else{
32056             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32057         }
32058         if(ttp){
32059             showProc = show.defer(tm.showDelay, tm, [{
32060                 el: t, 
32061                 text: ttp, 
32062                 width: et.getAttributeNS(ns, cfg.width),
32063                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32064                 title: et.getAttributeNS(ns, cfg.title),
32065                     cls: et.getAttributeNS(ns, cfg.cls)
32066             }]);
32067         }
32068     };
32069     
32070     var onOut = function(e){
32071         clearTimeout(showProc);
32072         var t = e.getTarget();
32073         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32074             hideProc = setTimeout(hide, tm.hideDelay);
32075         }
32076     };
32077     
32078     var onMove = function(e){
32079         if(disabled){
32080             return;
32081         }
32082         xy = e.getXY();
32083         xy[1] += 18;
32084         if(tm.trackMouse && ce){
32085             el.setXY(xy);
32086         }
32087     };
32088     
32089     var onDown = function(e){
32090         clearTimeout(showProc);
32091         clearTimeout(hideProc);
32092         if(!e.within(el)){
32093             if(tm.hideOnClick){
32094                 hide();
32095                 tm.disable();
32096                 tm.enable.defer(100, tm);
32097             }
32098         }
32099     };
32100     
32101     var getPad = function(){
32102         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32103     };
32104
32105     var show = function(o){
32106         if(disabled){
32107             return;
32108         }
32109         clearTimeout(dismissProc);
32110         ce = o;
32111         if(removeCls){ // in case manually hidden
32112             el.removeClass(removeCls);
32113             removeCls = null;
32114         }
32115         if(ce.cls){
32116             el.addClass(ce.cls);
32117             removeCls = ce.cls;
32118         }
32119         if(ce.title){
32120             tipTitle.update(ce.title);
32121             tipTitle.show();
32122         }else{
32123             tipTitle.update('');
32124             tipTitle.hide();
32125         }
32126         el.dom.style.width  = tm.maxWidth+'px';
32127         //tipBody.dom.style.width = '';
32128         tipBodyText.update(o.text);
32129         var p = getPad(), w = ce.width;
32130         if(!w){
32131             var td = tipBodyText.dom;
32132             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32133             if(aw > tm.maxWidth){
32134                 w = tm.maxWidth;
32135             }else if(aw < tm.minWidth){
32136                 w = tm.minWidth;
32137             }else{
32138                 w = aw;
32139             }
32140         }
32141         //tipBody.setWidth(w);
32142         el.setWidth(parseInt(w, 10) + p);
32143         if(ce.autoHide === false){
32144             close.setDisplayed(true);
32145             if(dd){
32146                 dd.unlock();
32147             }
32148         }else{
32149             close.setDisplayed(false);
32150             if(dd){
32151                 dd.lock();
32152             }
32153         }
32154         if(xy){
32155             el.avoidY = xy[1]-18;
32156             el.setXY(xy);
32157         }
32158         if(tm.animate){
32159             el.setOpacity(.1);
32160             el.setStyle("visibility", "visible");
32161             el.fadeIn({callback: afterShow});
32162         }else{
32163             afterShow();
32164         }
32165     };
32166     
32167     var afterShow = function(){
32168         if(ce){
32169             el.show();
32170             esc.enable();
32171             if(tm.autoDismiss && ce.autoHide !== false){
32172                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32173             }
32174         }
32175     };
32176     
32177     var hide = function(noanim){
32178         clearTimeout(dismissProc);
32179         clearTimeout(hideProc);
32180         ce = null;
32181         if(el.isVisible()){
32182             esc.disable();
32183             if(noanim !== true && tm.animate){
32184                 el.fadeOut({callback: afterHide});
32185             }else{
32186                 afterHide();
32187             } 
32188         }
32189     };
32190     
32191     var afterHide = function(){
32192         el.hide();
32193         if(removeCls){
32194             el.removeClass(removeCls);
32195             removeCls = null;
32196         }
32197     };
32198     
32199     return {
32200         /**
32201         * @cfg {Number} minWidth
32202         * The minimum width of the quick tip (defaults to 40)
32203         */
32204        minWidth : 40,
32205         /**
32206         * @cfg {Number} maxWidth
32207         * The maximum width of the quick tip (defaults to 300)
32208         */
32209        maxWidth : 300,
32210         /**
32211         * @cfg {Boolean} interceptTitles
32212         * True to automatically use the element's DOM title value if available (defaults to false)
32213         */
32214        interceptTitles : false,
32215         /**
32216         * @cfg {Boolean} trackMouse
32217         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32218         */
32219        trackMouse : false,
32220         /**
32221         * @cfg {Boolean} hideOnClick
32222         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32223         */
32224        hideOnClick : true,
32225         /**
32226         * @cfg {Number} showDelay
32227         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32228         */
32229        showDelay : 500,
32230         /**
32231         * @cfg {Number} hideDelay
32232         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32233         */
32234        hideDelay : 200,
32235         /**
32236         * @cfg {Boolean} autoHide
32237         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32238         * Used in conjunction with hideDelay.
32239         */
32240        autoHide : true,
32241         /**
32242         * @cfg {Boolean}
32243         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32244         * (defaults to true).  Used in conjunction with autoDismissDelay.
32245         */
32246        autoDismiss : true,
32247         /**
32248         * @cfg {Number}
32249         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32250         */
32251        autoDismissDelay : 5000,
32252        /**
32253         * @cfg {Boolean} animate
32254         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32255         */
32256        animate : false,
32257
32258        /**
32259         * @cfg {String} title
32260         * Title text to display (defaults to '').  This can be any valid HTML markup.
32261         */
32262         title: '',
32263        /**
32264         * @cfg {String} text
32265         * Body text to display (defaults to '').  This can be any valid HTML markup.
32266         */
32267         text : '',
32268        /**
32269         * @cfg {String} cls
32270         * A CSS class to apply to the base quick tip element (defaults to '').
32271         */
32272         cls : '',
32273        /**
32274         * @cfg {Number} width
32275         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32276         * minWidth or maxWidth.
32277         */
32278         width : null,
32279
32280     /**
32281      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32282      * or display QuickTips in a page.
32283      */
32284        init : function(){
32285           tm = Roo.QuickTips;
32286           cfg = tm.tagConfig;
32287           if(!inited){
32288               if(!Roo.isReady){ // allow calling of init() before onReady
32289                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32290                   return;
32291               }
32292               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32293               el.fxDefaults = {stopFx: true};
32294               // maximum custom styling
32295               //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>');
32296               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>');              
32297               tipTitle = el.child('h3');
32298               tipTitle.enableDisplayMode("block");
32299               tipBody = el.child('div.x-tip-bd');
32300               tipBodyText = el.child('div.x-tip-bd-inner');
32301               //bdLeft = el.child('div.x-tip-bd-left');
32302               //bdRight = el.child('div.x-tip-bd-right');
32303               close = el.child('div.x-tip-close');
32304               close.enableDisplayMode("block");
32305               close.on("click", hide);
32306               var d = Roo.get(document);
32307               d.on("mousedown", onDown);
32308               d.on("mouseover", onOver);
32309               d.on("mouseout", onOut);
32310               d.on("mousemove", onMove);
32311               esc = d.addKeyListener(27, hide);
32312               esc.disable();
32313               if(Roo.dd.DD){
32314                   dd = el.initDD("default", null, {
32315                       onDrag : function(){
32316                           el.sync();  
32317                       }
32318                   });
32319                   dd.setHandleElId(tipTitle.id);
32320                   dd.lock();
32321               }
32322               inited = true;
32323           }
32324           this.enable(); 
32325        },
32326
32327     /**
32328      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32329      * are supported:
32330      * <pre>
32331 Property    Type                   Description
32332 ----------  ---------------------  ------------------------------------------------------------------------
32333 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32334      * </ul>
32335      * @param {Object} config The config object
32336      */
32337        register : function(config){
32338            var cs = config instanceof Array ? config : arguments;
32339            for(var i = 0, len = cs.length; i < len; i++) {
32340                var c = cs[i];
32341                var target = c.target;
32342                if(target){
32343                    if(target instanceof Array){
32344                        for(var j = 0, jlen = target.length; j < jlen; j++){
32345                            tagEls[target[j]] = c;
32346                        }
32347                    }else{
32348                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32349                    }
32350                }
32351            }
32352        },
32353
32354     /**
32355      * Removes this quick tip from its element and destroys it.
32356      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32357      */
32358        unregister : function(el){
32359            delete tagEls[Roo.id(el)];
32360        },
32361
32362     /**
32363      * Enable this quick tip.
32364      */
32365        enable : function(){
32366            if(inited && disabled){
32367                locks.pop();
32368                if(locks.length < 1){
32369                    disabled = false;
32370                }
32371            }
32372        },
32373
32374     /**
32375      * Disable this quick tip.
32376      */
32377        disable : function(){
32378           disabled = true;
32379           clearTimeout(showProc);
32380           clearTimeout(hideProc);
32381           clearTimeout(dismissProc);
32382           if(ce){
32383               hide(true);
32384           }
32385           locks.push(1);
32386        },
32387
32388     /**
32389      * Returns true if the quick tip is enabled, else false.
32390      */
32391        isEnabled : function(){
32392             return !disabled;
32393        },
32394
32395         // private
32396        tagConfig : {
32397            namespace : "ext",
32398            attribute : "qtip",
32399            width : "width",
32400            target : "target",
32401            title : "qtitle",
32402            hide : "hide",
32403            cls : "qclass"
32404        }
32405    };
32406 }();
32407
32408 // backwards compat
32409 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32410  * Based on:
32411  * Ext JS Library 1.1.1
32412  * Copyright(c) 2006-2007, Ext JS, LLC.
32413  *
32414  * Originally Released Under LGPL - original licence link has changed is not relivant.
32415  *
32416  * Fork - LGPL
32417  * <script type="text/javascript">
32418  */
32419  
32420
32421 /**
32422  * @class Roo.tree.TreePanel
32423  * @extends Roo.data.Tree
32424
32425  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32426  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32427  * @cfg {Boolean} enableDD true to enable drag and drop
32428  * @cfg {Boolean} enableDrag true to enable just drag
32429  * @cfg {Boolean} enableDrop true to enable just drop
32430  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32431  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32432  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32433  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32434  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32435  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32436  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32437  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32438  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32439  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32440  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32441  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32442  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32443  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32444  * @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>
32445  * @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>
32446  * 
32447  * @constructor
32448  * @param {String/HTMLElement/Element} el The container element
32449  * @param {Object} config
32450  */
32451 Roo.tree.TreePanel = function(el, config){
32452     var root = false;
32453     var loader = false;
32454     if (config.root) {
32455         root = config.root;
32456         delete config.root;
32457     }
32458     if (config.loader) {
32459         loader = config.loader;
32460         delete config.loader;
32461     }
32462     
32463     Roo.apply(this, config);
32464     Roo.tree.TreePanel.superclass.constructor.call(this);
32465     this.el = Roo.get(el);
32466     this.el.addClass('x-tree');
32467     //console.log(root);
32468     if (root) {
32469         this.setRootNode( Roo.factory(root, Roo.tree));
32470     }
32471     if (loader) {
32472         this.loader = Roo.factory(loader, Roo.tree);
32473     }
32474    /**
32475     * Read-only. The id of the container element becomes this TreePanel's id.
32476     */
32477     this.id = this.el.id;
32478     this.addEvents({
32479         /**
32480         * @event beforeload
32481         * Fires before a node is loaded, return false to cancel
32482         * @param {Node} node The node being loaded
32483         */
32484         "beforeload" : true,
32485         /**
32486         * @event load
32487         * Fires when a node is loaded
32488         * @param {Node} node The node that was loaded
32489         */
32490         "load" : true,
32491         /**
32492         * @event textchange
32493         * Fires when the text for a node is changed
32494         * @param {Node} node The node
32495         * @param {String} text The new text
32496         * @param {String} oldText The old text
32497         */
32498         "textchange" : true,
32499         /**
32500         * @event beforeexpand
32501         * Fires before a node is expanded, return false to cancel.
32502         * @param {Node} node The node
32503         * @param {Boolean} deep
32504         * @param {Boolean} anim
32505         */
32506         "beforeexpand" : true,
32507         /**
32508         * @event beforecollapse
32509         * Fires before a node is collapsed, return false to cancel.
32510         * @param {Node} node The node
32511         * @param {Boolean} deep
32512         * @param {Boolean} anim
32513         */
32514         "beforecollapse" : true,
32515         /**
32516         * @event expand
32517         * Fires when a node is expanded
32518         * @param {Node} node The node
32519         */
32520         "expand" : true,
32521         /**
32522         * @event disabledchange
32523         * Fires when the disabled status of a node changes
32524         * @param {Node} node The node
32525         * @param {Boolean} disabled
32526         */
32527         "disabledchange" : true,
32528         /**
32529         * @event collapse
32530         * Fires when a node is collapsed
32531         * @param {Node} node The node
32532         */
32533         "collapse" : true,
32534         /**
32535         * @event beforeclick
32536         * Fires before click processing on a node. Return false to cancel the default action.
32537         * @param {Node} node The node
32538         * @param {Roo.EventObject} e The event object
32539         */
32540         "beforeclick":true,
32541         /**
32542         * @event checkchange
32543         * Fires when a node with a checkbox's checked property changes
32544         * @param {Node} this This node
32545         * @param {Boolean} checked
32546         */
32547         "checkchange":true,
32548         /**
32549         * @event click
32550         * Fires when a node is clicked
32551         * @param {Node} node The node
32552         * @param {Roo.EventObject} e The event object
32553         */
32554         "click":true,
32555         /**
32556         * @event dblclick
32557         * Fires when a node is double clicked
32558         * @param {Node} node The node
32559         * @param {Roo.EventObject} e The event object
32560         */
32561         "dblclick":true,
32562         /**
32563         * @event contextmenu
32564         * Fires when a node is right clicked
32565         * @param {Node} node The node
32566         * @param {Roo.EventObject} e The event object
32567         */
32568         "contextmenu":true,
32569         /**
32570         * @event beforechildrenrendered
32571         * Fires right before the child nodes for a node are rendered
32572         * @param {Node} node The node
32573         */
32574         "beforechildrenrendered":true,
32575         /**
32576         * @event startdrag
32577         * Fires when a node starts being dragged
32578         * @param {Roo.tree.TreePanel} this
32579         * @param {Roo.tree.TreeNode} node
32580         * @param {event} e The raw browser event
32581         */ 
32582        "startdrag" : true,
32583        /**
32584         * @event enddrag
32585         * Fires when a drag operation is complete
32586         * @param {Roo.tree.TreePanel} this
32587         * @param {Roo.tree.TreeNode} node
32588         * @param {event} e The raw browser event
32589         */
32590        "enddrag" : true,
32591        /**
32592         * @event dragdrop
32593         * Fires when a dragged node is dropped on a valid DD target
32594         * @param {Roo.tree.TreePanel} this
32595         * @param {Roo.tree.TreeNode} node
32596         * @param {DD} dd The dd it was dropped on
32597         * @param {event} e The raw browser event
32598         */
32599        "dragdrop" : true,
32600        /**
32601         * @event beforenodedrop
32602         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32603         * passed to handlers has the following properties:<br />
32604         * <ul style="padding:5px;padding-left:16px;">
32605         * <li>tree - The TreePanel</li>
32606         * <li>target - The node being targeted for the drop</li>
32607         * <li>data - The drag data from the drag source</li>
32608         * <li>point - The point of the drop - append, above or below</li>
32609         * <li>source - The drag source</li>
32610         * <li>rawEvent - Raw mouse event</li>
32611         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32612         * to be inserted by setting them on this object.</li>
32613         * <li>cancel - Set this to true to cancel the drop.</li>
32614         * </ul>
32615         * @param {Object} dropEvent
32616         */
32617        "beforenodedrop" : true,
32618        /**
32619         * @event nodedrop
32620         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32621         * passed to handlers has the following properties:<br />
32622         * <ul style="padding:5px;padding-left:16px;">
32623         * <li>tree - The TreePanel</li>
32624         * <li>target - The node being targeted for the drop</li>
32625         * <li>data - The drag data from the drag source</li>
32626         * <li>point - The point of the drop - append, above or below</li>
32627         * <li>source - The drag source</li>
32628         * <li>rawEvent - Raw mouse event</li>
32629         * <li>dropNode - Dropped node(s).</li>
32630         * </ul>
32631         * @param {Object} dropEvent
32632         */
32633        "nodedrop" : true,
32634         /**
32635         * @event nodedragover
32636         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32637         * passed to handlers has the following properties:<br />
32638         * <ul style="padding:5px;padding-left:16px;">
32639         * <li>tree - The TreePanel</li>
32640         * <li>target - The node being targeted for the drop</li>
32641         * <li>data - The drag data from the drag source</li>
32642         * <li>point - The point of the drop - append, above or below</li>
32643         * <li>source - The drag source</li>
32644         * <li>rawEvent - Raw mouse event</li>
32645         * <li>dropNode - Drop node(s) provided by the source.</li>
32646         * <li>cancel - Set this to true to signal drop not allowed.</li>
32647         * </ul>
32648         * @param {Object} dragOverEvent
32649         */
32650        "nodedragover" : true
32651         
32652     });
32653     if(this.singleExpand){
32654        this.on("beforeexpand", this.restrictExpand, this);
32655     }
32656     if (this.editor) {
32657         this.editor.tree = this;
32658         this.editor = Roo.factory(this.editor, Roo.tree);
32659     }
32660     
32661     if (this.selModel) {
32662         this.selModel = Roo.factory(this.selModel, Roo.tree);
32663     }
32664    
32665 };
32666 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32667     rootVisible : true,
32668     animate: Roo.enableFx,
32669     lines : true,
32670     enableDD : false,
32671     hlDrop : Roo.enableFx,
32672   
32673     renderer: false,
32674     
32675     rendererTip: false,
32676     // private
32677     restrictExpand : function(node){
32678         var p = node.parentNode;
32679         if(p){
32680             if(p.expandedChild && p.expandedChild.parentNode == p){
32681                 p.expandedChild.collapse();
32682             }
32683             p.expandedChild = node;
32684         }
32685     },
32686
32687     // private override
32688     setRootNode : function(node){
32689         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32690         if(!this.rootVisible){
32691             node.ui = new Roo.tree.RootTreeNodeUI(node);
32692         }
32693         return node;
32694     },
32695
32696     /**
32697      * Returns the container element for this TreePanel
32698      */
32699     getEl : function(){
32700         return this.el;
32701     },
32702
32703     /**
32704      * Returns the default TreeLoader for this TreePanel
32705      */
32706     getLoader : function(){
32707         return this.loader;
32708     },
32709
32710     /**
32711      * Expand all nodes
32712      */
32713     expandAll : function(){
32714         this.root.expand(true);
32715     },
32716
32717     /**
32718      * Collapse all nodes
32719      */
32720     collapseAll : function(){
32721         this.root.collapse(true);
32722     },
32723
32724     /**
32725      * Returns the selection model used by this TreePanel
32726      */
32727     getSelectionModel : function(){
32728         if(!this.selModel){
32729             this.selModel = new Roo.tree.DefaultSelectionModel();
32730         }
32731         return this.selModel;
32732     },
32733
32734     /**
32735      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32736      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32737      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32738      * @return {Array}
32739      */
32740     getChecked : function(a, startNode){
32741         startNode = startNode || this.root;
32742         var r = [];
32743         var f = function(){
32744             if(this.attributes.checked){
32745                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32746             }
32747         }
32748         startNode.cascade(f);
32749         return r;
32750     },
32751
32752     /**
32753      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32754      * @param {String} path
32755      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32756      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32757      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32758      */
32759     expandPath : function(path, attr, callback){
32760         attr = attr || "id";
32761         var keys = path.split(this.pathSeparator);
32762         var curNode = this.root;
32763         if(curNode.attributes[attr] != keys[1]){ // invalid root
32764             if(callback){
32765                 callback(false, null);
32766             }
32767             return;
32768         }
32769         var index = 1;
32770         var f = function(){
32771             if(++index == keys.length){
32772                 if(callback){
32773                     callback(true, curNode);
32774                 }
32775                 return;
32776             }
32777             var c = curNode.findChild(attr, keys[index]);
32778             if(!c){
32779                 if(callback){
32780                     callback(false, curNode);
32781                 }
32782                 return;
32783             }
32784             curNode = c;
32785             c.expand(false, false, f);
32786         };
32787         curNode.expand(false, false, f);
32788     },
32789
32790     /**
32791      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32792      * @param {String} path
32793      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32794      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32795      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32796      */
32797     selectPath : function(path, attr, callback){
32798         attr = attr || "id";
32799         var keys = path.split(this.pathSeparator);
32800         var v = keys.pop();
32801         if(keys.length > 0){
32802             var f = function(success, node){
32803                 if(success && node){
32804                     var n = node.findChild(attr, v);
32805                     if(n){
32806                         n.select();
32807                         if(callback){
32808                             callback(true, n);
32809                         }
32810                     }else if(callback){
32811                         callback(false, n);
32812                     }
32813                 }else{
32814                     if(callback){
32815                         callback(false, n);
32816                     }
32817                 }
32818             };
32819             this.expandPath(keys.join(this.pathSeparator), attr, f);
32820         }else{
32821             this.root.select();
32822             if(callback){
32823                 callback(true, this.root);
32824             }
32825         }
32826     },
32827
32828     getTreeEl : function(){
32829         return this.el;
32830     },
32831
32832     /**
32833      * Trigger rendering of this TreePanel
32834      */
32835     render : function(){
32836         if (this.innerCt) {
32837             return this; // stop it rendering more than once!!
32838         }
32839         
32840         this.innerCt = this.el.createChild({tag:"ul",
32841                cls:"x-tree-root-ct " +
32842                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32843
32844         if(this.containerScroll){
32845             Roo.dd.ScrollManager.register(this.el);
32846         }
32847         if((this.enableDD || this.enableDrop) && !this.dropZone){
32848            /**
32849             * The dropZone used by this tree if drop is enabled
32850             * @type Roo.tree.TreeDropZone
32851             */
32852              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32853                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32854            });
32855         }
32856         if((this.enableDD || this.enableDrag) && !this.dragZone){
32857            /**
32858             * The dragZone used by this tree if drag is enabled
32859             * @type Roo.tree.TreeDragZone
32860             */
32861             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32862                ddGroup: this.ddGroup || "TreeDD",
32863                scroll: this.ddScroll
32864            });
32865         }
32866         this.getSelectionModel().init(this);
32867         if (!this.root) {
32868             Roo.log("ROOT not set in tree");
32869             return this;
32870         }
32871         this.root.render();
32872         if(!this.rootVisible){
32873             this.root.renderChildren();
32874         }
32875         return this;
32876     }
32877 });/*
32878  * Based on:
32879  * Ext JS Library 1.1.1
32880  * Copyright(c) 2006-2007, Ext JS, LLC.
32881  *
32882  * Originally Released Under LGPL - original licence link has changed is not relivant.
32883  *
32884  * Fork - LGPL
32885  * <script type="text/javascript">
32886  */
32887  
32888
32889 /**
32890  * @class Roo.tree.DefaultSelectionModel
32891  * @extends Roo.util.Observable
32892  * The default single selection for a TreePanel.
32893  * @param {Object} cfg Configuration
32894  */
32895 Roo.tree.DefaultSelectionModel = function(cfg){
32896    this.selNode = null;
32897    
32898    
32899    
32900    this.addEvents({
32901        /**
32902         * @event selectionchange
32903         * Fires when the selected node changes
32904         * @param {DefaultSelectionModel} this
32905         * @param {TreeNode} node the new selection
32906         */
32907        "selectionchange" : true,
32908
32909        /**
32910         * @event beforeselect
32911         * Fires before the selected node changes, return false to cancel the change
32912         * @param {DefaultSelectionModel} this
32913         * @param {TreeNode} node the new selection
32914         * @param {TreeNode} node the old selection
32915         */
32916        "beforeselect" : true
32917    });
32918    
32919     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32920 };
32921
32922 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32923     init : function(tree){
32924         this.tree = tree;
32925         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32926         tree.on("click", this.onNodeClick, this);
32927     },
32928     
32929     onNodeClick : function(node, e){
32930         if (e.ctrlKey && this.selNode == node)  {
32931             this.unselect(node);
32932             return;
32933         }
32934         this.select(node);
32935     },
32936     
32937     /**
32938      * Select a node.
32939      * @param {TreeNode} node The node to select
32940      * @return {TreeNode} The selected node
32941      */
32942     select : function(node){
32943         var last = this.selNode;
32944         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32945             if(last){
32946                 last.ui.onSelectedChange(false);
32947             }
32948             this.selNode = node;
32949             node.ui.onSelectedChange(true);
32950             this.fireEvent("selectionchange", this, node, last);
32951         }
32952         return node;
32953     },
32954     
32955     /**
32956      * Deselect a node.
32957      * @param {TreeNode} node The node to unselect
32958      */
32959     unselect : function(node){
32960         if(this.selNode == node){
32961             this.clearSelections();
32962         }    
32963     },
32964     
32965     /**
32966      * Clear all selections
32967      */
32968     clearSelections : function(){
32969         var n = this.selNode;
32970         if(n){
32971             n.ui.onSelectedChange(false);
32972             this.selNode = null;
32973             this.fireEvent("selectionchange", this, null);
32974         }
32975         return n;
32976     },
32977     
32978     /**
32979      * Get the selected node
32980      * @return {TreeNode} The selected node
32981      */
32982     getSelectedNode : function(){
32983         return this.selNode;    
32984     },
32985     
32986     /**
32987      * Returns true if the node is selected
32988      * @param {TreeNode} node The node to check
32989      * @return {Boolean}
32990      */
32991     isSelected : function(node){
32992         return this.selNode == node;  
32993     },
32994
32995     /**
32996      * Selects the node above the selected node in the tree, intelligently walking the nodes
32997      * @return TreeNode The new selection
32998      */
32999     selectPrevious : function(){
33000         var s = this.selNode || this.lastSelNode;
33001         if(!s){
33002             return null;
33003         }
33004         var ps = s.previousSibling;
33005         if(ps){
33006             if(!ps.isExpanded() || ps.childNodes.length < 1){
33007                 return this.select(ps);
33008             } else{
33009                 var lc = ps.lastChild;
33010                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
33011                     lc = lc.lastChild;
33012                 }
33013                 return this.select(lc);
33014             }
33015         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
33016             return this.select(s.parentNode);
33017         }
33018         return null;
33019     },
33020
33021     /**
33022      * Selects the node above the selected node in the tree, intelligently walking the nodes
33023      * @return TreeNode The new selection
33024      */
33025     selectNext : function(){
33026         var s = this.selNode || this.lastSelNode;
33027         if(!s){
33028             return null;
33029         }
33030         if(s.firstChild && s.isExpanded()){
33031              return this.select(s.firstChild);
33032          }else if(s.nextSibling){
33033              return this.select(s.nextSibling);
33034          }else if(s.parentNode){
33035             var newS = null;
33036             s.parentNode.bubble(function(){
33037                 if(this.nextSibling){
33038                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
33039                     return false;
33040                 }
33041             });
33042             return newS;
33043          }
33044         return null;
33045     },
33046
33047     onKeyDown : function(e){
33048         var s = this.selNode || this.lastSelNode;
33049         // undesirable, but required
33050         var sm = this;
33051         if(!s){
33052             return;
33053         }
33054         var k = e.getKey();
33055         switch(k){
33056              case e.DOWN:
33057                  e.stopEvent();
33058                  this.selectNext();
33059              break;
33060              case e.UP:
33061                  e.stopEvent();
33062                  this.selectPrevious();
33063              break;
33064              case e.RIGHT:
33065                  e.preventDefault();
33066                  if(s.hasChildNodes()){
33067                      if(!s.isExpanded()){
33068                          s.expand();
33069                      }else if(s.firstChild){
33070                          this.select(s.firstChild, e);
33071                      }
33072                  }
33073              break;
33074              case e.LEFT:
33075                  e.preventDefault();
33076                  if(s.hasChildNodes() && s.isExpanded()){
33077                      s.collapse();
33078                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33079                      this.select(s.parentNode, e);
33080                  }
33081              break;
33082         };
33083     }
33084 });
33085
33086 /**
33087  * @class Roo.tree.MultiSelectionModel
33088  * @extends Roo.util.Observable
33089  * Multi selection for a TreePanel.
33090  * @param {Object} cfg Configuration
33091  */
33092 Roo.tree.MultiSelectionModel = function(){
33093    this.selNodes = [];
33094    this.selMap = {};
33095    this.addEvents({
33096        /**
33097         * @event selectionchange
33098         * Fires when the selected nodes change
33099         * @param {MultiSelectionModel} this
33100         * @param {Array} nodes Array of the selected nodes
33101         */
33102        "selectionchange" : true
33103    });
33104    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33105    
33106 };
33107
33108 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33109     init : function(tree){
33110         this.tree = tree;
33111         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33112         tree.on("click", this.onNodeClick, this);
33113     },
33114     
33115     onNodeClick : function(node, e){
33116         this.select(node, e, e.ctrlKey);
33117     },
33118     
33119     /**
33120      * Select a node.
33121      * @param {TreeNode} node The node to select
33122      * @param {EventObject} e (optional) An event associated with the selection
33123      * @param {Boolean} keepExisting True to retain existing selections
33124      * @return {TreeNode} The selected node
33125      */
33126     select : function(node, e, keepExisting){
33127         if(keepExisting !== true){
33128             this.clearSelections(true);
33129         }
33130         if(this.isSelected(node)){
33131             this.lastSelNode = node;
33132             return node;
33133         }
33134         this.selNodes.push(node);
33135         this.selMap[node.id] = node;
33136         this.lastSelNode = node;
33137         node.ui.onSelectedChange(true);
33138         this.fireEvent("selectionchange", this, this.selNodes);
33139         return node;
33140     },
33141     
33142     /**
33143      * Deselect a node.
33144      * @param {TreeNode} node The node to unselect
33145      */
33146     unselect : function(node){
33147         if(this.selMap[node.id]){
33148             node.ui.onSelectedChange(false);
33149             var sn = this.selNodes;
33150             var index = -1;
33151             if(sn.indexOf){
33152                 index = sn.indexOf(node);
33153             }else{
33154                 for(var i = 0, len = sn.length; i < len; i++){
33155                     if(sn[i] == node){
33156                         index = i;
33157                         break;
33158                     }
33159                 }
33160             }
33161             if(index != -1){
33162                 this.selNodes.splice(index, 1);
33163             }
33164             delete this.selMap[node.id];
33165             this.fireEvent("selectionchange", this, this.selNodes);
33166         }
33167     },
33168     
33169     /**
33170      * Clear all selections
33171      */
33172     clearSelections : function(suppressEvent){
33173         var sn = this.selNodes;
33174         if(sn.length > 0){
33175             for(var i = 0, len = sn.length; i < len; i++){
33176                 sn[i].ui.onSelectedChange(false);
33177             }
33178             this.selNodes = [];
33179             this.selMap = {};
33180             if(suppressEvent !== true){
33181                 this.fireEvent("selectionchange", this, this.selNodes);
33182             }
33183         }
33184     },
33185     
33186     /**
33187      * Returns true if the node is selected
33188      * @param {TreeNode} node The node to check
33189      * @return {Boolean}
33190      */
33191     isSelected : function(node){
33192         return this.selMap[node.id] ? true : false;  
33193     },
33194     
33195     /**
33196      * Returns an array of the selected nodes
33197      * @return {Array}
33198      */
33199     getSelectedNodes : function(){
33200         return this.selNodes;    
33201     },
33202
33203     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33204
33205     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33206
33207     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33208 });/*
33209  * Based on:
33210  * Ext JS Library 1.1.1
33211  * Copyright(c) 2006-2007, Ext JS, LLC.
33212  *
33213  * Originally Released Under LGPL - original licence link has changed is not relivant.
33214  *
33215  * Fork - LGPL
33216  * <script type="text/javascript">
33217  */
33218  
33219 /**
33220  * @class Roo.tree.TreeNode
33221  * @extends Roo.data.Node
33222  * @cfg {String} text The text for this node
33223  * @cfg {Boolean} expanded true to start the node expanded
33224  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33225  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33226  * @cfg {Boolean} disabled true to start the node disabled
33227  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33228  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33229  * @cfg {String} cls A css class to be added to the node
33230  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33231  * @cfg {String} href URL of the link used for the node (defaults to #)
33232  * @cfg {String} hrefTarget target frame for the link
33233  * @cfg {String} qtip An Ext QuickTip for the node
33234  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33235  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33236  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33237  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33238  * (defaults to undefined with no checkbox rendered)
33239  * @constructor
33240  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33241  */
33242 Roo.tree.TreeNode = function(attributes){
33243     attributes = attributes || {};
33244     if(typeof attributes == "string"){
33245         attributes = {text: attributes};
33246     }
33247     this.childrenRendered = false;
33248     this.rendered = false;
33249     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33250     this.expanded = attributes.expanded === true;
33251     this.isTarget = attributes.isTarget !== false;
33252     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33253     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33254
33255     /**
33256      * Read-only. The text for this node. To change it use setText().
33257      * @type String
33258      */
33259     this.text = attributes.text;
33260     /**
33261      * True if this node is disabled.
33262      * @type Boolean
33263      */
33264     this.disabled = attributes.disabled === true;
33265
33266     this.addEvents({
33267         /**
33268         * @event textchange
33269         * Fires when the text for this node is changed
33270         * @param {Node} this This node
33271         * @param {String} text The new text
33272         * @param {String} oldText The old text
33273         */
33274         "textchange" : true,
33275         /**
33276         * @event beforeexpand
33277         * Fires before this node is expanded, return false to cancel.
33278         * @param {Node} this This node
33279         * @param {Boolean} deep
33280         * @param {Boolean} anim
33281         */
33282         "beforeexpand" : true,
33283         /**
33284         * @event beforecollapse
33285         * Fires before this node is collapsed, return false to cancel.
33286         * @param {Node} this This node
33287         * @param {Boolean} deep
33288         * @param {Boolean} anim
33289         */
33290         "beforecollapse" : true,
33291         /**
33292         * @event expand
33293         * Fires when this node is expanded
33294         * @param {Node} this This node
33295         */
33296         "expand" : true,
33297         /**
33298         * @event disabledchange
33299         * Fires when the disabled status of this node changes
33300         * @param {Node} this This node
33301         * @param {Boolean} disabled
33302         */
33303         "disabledchange" : true,
33304         /**
33305         * @event collapse
33306         * Fires when this node is collapsed
33307         * @param {Node} this This node
33308         */
33309         "collapse" : true,
33310         /**
33311         * @event beforeclick
33312         * Fires before click processing. Return false to cancel the default action.
33313         * @param {Node} this This node
33314         * @param {Roo.EventObject} e The event object
33315         */
33316         "beforeclick":true,
33317         /**
33318         * @event checkchange
33319         * Fires when a node with a checkbox's checked property changes
33320         * @param {Node} this This node
33321         * @param {Boolean} checked
33322         */
33323         "checkchange":true,
33324         /**
33325         * @event click
33326         * Fires when this node is clicked
33327         * @param {Node} this This node
33328         * @param {Roo.EventObject} e The event object
33329         */
33330         "click":true,
33331         /**
33332         * @event dblclick
33333         * Fires when this node is double clicked
33334         * @param {Node} this This node
33335         * @param {Roo.EventObject} e The event object
33336         */
33337         "dblclick":true,
33338         /**
33339         * @event contextmenu
33340         * Fires when this node is right clicked
33341         * @param {Node} this This node
33342         * @param {Roo.EventObject} e The event object
33343         */
33344         "contextmenu":true,
33345         /**
33346         * @event beforechildrenrendered
33347         * Fires right before the child nodes for this node are rendered
33348         * @param {Node} this This node
33349         */
33350         "beforechildrenrendered":true
33351     });
33352
33353     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33354
33355     /**
33356      * Read-only. The UI for this node
33357      * @type TreeNodeUI
33358      */
33359     this.ui = new uiClass(this);
33360     
33361     // finally support items[]
33362     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33363         return;
33364     }
33365     
33366     
33367     Roo.each(this.attributes.items, function(c) {
33368         this.appendChild(Roo.factory(c,Roo.Tree));
33369     }, this);
33370     delete this.attributes.items;
33371     
33372     
33373     
33374 };
33375 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33376     preventHScroll: true,
33377     /**
33378      * Returns true if this node is expanded
33379      * @return {Boolean}
33380      */
33381     isExpanded : function(){
33382         return this.expanded;
33383     },
33384
33385     /**
33386      * Returns the UI object for this node
33387      * @return {TreeNodeUI}
33388      */
33389     getUI : function(){
33390         return this.ui;
33391     },
33392
33393     // private override
33394     setFirstChild : function(node){
33395         var of = this.firstChild;
33396         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33397         if(this.childrenRendered && of && node != of){
33398             of.renderIndent(true, true);
33399         }
33400         if(this.rendered){
33401             this.renderIndent(true, true);
33402         }
33403     },
33404
33405     // private override
33406     setLastChild : function(node){
33407         var ol = this.lastChild;
33408         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33409         if(this.childrenRendered && ol && node != ol){
33410             ol.renderIndent(true, true);
33411         }
33412         if(this.rendered){
33413             this.renderIndent(true, true);
33414         }
33415     },
33416
33417     // these methods are overridden to provide lazy rendering support
33418     // private override
33419     appendChild : function()
33420     {
33421         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33422         if(node && this.childrenRendered){
33423             node.render();
33424         }
33425         this.ui.updateExpandIcon();
33426         return node;
33427     },
33428
33429     // private override
33430     removeChild : function(node){
33431         this.ownerTree.getSelectionModel().unselect(node);
33432         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33433         // if it's been rendered remove dom node
33434         if(this.childrenRendered){
33435             node.ui.remove();
33436         }
33437         if(this.childNodes.length < 1){
33438             this.collapse(false, false);
33439         }else{
33440             this.ui.updateExpandIcon();
33441         }
33442         if(!this.firstChild) {
33443             this.childrenRendered = false;
33444         }
33445         return node;
33446     },
33447
33448     // private override
33449     insertBefore : function(node, refNode){
33450         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33451         if(newNode && refNode && this.childrenRendered){
33452             node.render();
33453         }
33454         this.ui.updateExpandIcon();
33455         return newNode;
33456     },
33457
33458     /**
33459      * Sets the text for this node
33460      * @param {String} text
33461      */
33462     setText : function(text){
33463         var oldText = this.text;
33464         this.text = text;
33465         this.attributes.text = text;
33466         if(this.rendered){ // event without subscribing
33467             this.ui.onTextChange(this, text, oldText);
33468         }
33469         this.fireEvent("textchange", this, text, oldText);
33470     },
33471
33472     /**
33473      * Triggers selection of this node
33474      */
33475     select : function(){
33476         this.getOwnerTree().getSelectionModel().select(this);
33477     },
33478
33479     /**
33480      * Triggers deselection of this node
33481      */
33482     unselect : function(){
33483         this.getOwnerTree().getSelectionModel().unselect(this);
33484     },
33485
33486     /**
33487      * Returns true if this node is selected
33488      * @return {Boolean}
33489      */
33490     isSelected : function(){
33491         return this.getOwnerTree().getSelectionModel().isSelected(this);
33492     },
33493
33494     /**
33495      * Expand this node.
33496      * @param {Boolean} deep (optional) True to expand all children as well
33497      * @param {Boolean} anim (optional) false to cancel the default animation
33498      * @param {Function} callback (optional) A callback to be called when
33499      * expanding this node completes (does not wait for deep expand to complete).
33500      * Called with 1 parameter, this node.
33501      */
33502     expand : function(deep, anim, callback){
33503         if(!this.expanded){
33504             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33505                 return;
33506             }
33507             if(!this.childrenRendered){
33508                 this.renderChildren();
33509             }
33510             this.expanded = true;
33511             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33512                 this.ui.animExpand(function(){
33513                     this.fireEvent("expand", this);
33514                     if(typeof callback == "function"){
33515                         callback(this);
33516                     }
33517                     if(deep === true){
33518                         this.expandChildNodes(true);
33519                     }
33520                 }.createDelegate(this));
33521                 return;
33522             }else{
33523                 this.ui.expand();
33524                 this.fireEvent("expand", this);
33525                 if(typeof callback == "function"){
33526                     callback(this);
33527                 }
33528             }
33529         }else{
33530            if(typeof callback == "function"){
33531                callback(this);
33532            }
33533         }
33534         if(deep === true){
33535             this.expandChildNodes(true);
33536         }
33537     },
33538
33539     isHiddenRoot : function(){
33540         return this.isRoot && !this.getOwnerTree().rootVisible;
33541     },
33542
33543     /**
33544      * Collapse this node.
33545      * @param {Boolean} deep (optional) True to collapse all children as well
33546      * @param {Boolean} anim (optional) false to cancel the default animation
33547      */
33548     collapse : function(deep, anim){
33549         if(this.expanded && !this.isHiddenRoot()){
33550             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33551                 return;
33552             }
33553             this.expanded = false;
33554             if((this.getOwnerTree().animate && anim !== false) || anim){
33555                 this.ui.animCollapse(function(){
33556                     this.fireEvent("collapse", this);
33557                     if(deep === true){
33558                         this.collapseChildNodes(true);
33559                     }
33560                 }.createDelegate(this));
33561                 return;
33562             }else{
33563                 this.ui.collapse();
33564                 this.fireEvent("collapse", this);
33565             }
33566         }
33567         if(deep === true){
33568             var cs = this.childNodes;
33569             for(var i = 0, len = cs.length; i < len; i++) {
33570                 cs[i].collapse(true, false);
33571             }
33572         }
33573     },
33574
33575     // private
33576     delayedExpand : function(delay){
33577         if(!this.expandProcId){
33578             this.expandProcId = this.expand.defer(delay, this);
33579         }
33580     },
33581
33582     // private
33583     cancelExpand : function(){
33584         if(this.expandProcId){
33585             clearTimeout(this.expandProcId);
33586         }
33587         this.expandProcId = false;
33588     },
33589
33590     /**
33591      * Toggles expanded/collapsed state of the node
33592      */
33593     toggle : function(){
33594         if(this.expanded){
33595             this.collapse();
33596         }else{
33597             this.expand();
33598         }
33599     },
33600
33601     /**
33602      * Ensures all parent nodes are expanded
33603      */
33604     ensureVisible : function(callback){
33605         var tree = this.getOwnerTree();
33606         tree.expandPath(this.parentNode.getPath(), false, function(){
33607             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33608             Roo.callback(callback);
33609         }.createDelegate(this));
33610     },
33611
33612     /**
33613      * Expand all child nodes
33614      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33615      */
33616     expandChildNodes : function(deep){
33617         var cs = this.childNodes;
33618         for(var i = 0, len = cs.length; i < len; i++) {
33619                 cs[i].expand(deep);
33620         }
33621     },
33622
33623     /**
33624      * Collapse all child nodes
33625      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33626      */
33627     collapseChildNodes : function(deep){
33628         var cs = this.childNodes;
33629         for(var i = 0, len = cs.length; i < len; i++) {
33630                 cs[i].collapse(deep);
33631         }
33632     },
33633
33634     /**
33635      * Disables this node
33636      */
33637     disable : function(){
33638         this.disabled = true;
33639         this.unselect();
33640         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33641             this.ui.onDisableChange(this, true);
33642         }
33643         this.fireEvent("disabledchange", this, true);
33644     },
33645
33646     /**
33647      * Enables this node
33648      */
33649     enable : function(){
33650         this.disabled = false;
33651         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33652             this.ui.onDisableChange(this, false);
33653         }
33654         this.fireEvent("disabledchange", this, false);
33655     },
33656
33657     // private
33658     renderChildren : function(suppressEvent){
33659         if(suppressEvent !== false){
33660             this.fireEvent("beforechildrenrendered", this);
33661         }
33662         var cs = this.childNodes;
33663         for(var i = 0, len = cs.length; i < len; i++){
33664             cs[i].render(true);
33665         }
33666         this.childrenRendered = true;
33667     },
33668
33669     // private
33670     sort : function(fn, scope){
33671         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33672         if(this.childrenRendered){
33673             var cs = this.childNodes;
33674             for(var i = 0, len = cs.length; i < len; i++){
33675                 cs[i].render(true);
33676             }
33677         }
33678     },
33679
33680     // private
33681     render : function(bulkRender){
33682         this.ui.render(bulkRender);
33683         if(!this.rendered){
33684             this.rendered = true;
33685             if(this.expanded){
33686                 this.expanded = false;
33687                 this.expand(false, false);
33688             }
33689         }
33690     },
33691
33692     // private
33693     renderIndent : function(deep, refresh){
33694         if(refresh){
33695             this.ui.childIndent = null;
33696         }
33697         this.ui.renderIndent();
33698         if(deep === true && this.childrenRendered){
33699             var cs = this.childNodes;
33700             for(var i = 0, len = cs.length; i < len; i++){
33701                 cs[i].renderIndent(true, refresh);
33702             }
33703         }
33704     }
33705 });/*
33706  * Based on:
33707  * Ext JS Library 1.1.1
33708  * Copyright(c) 2006-2007, Ext JS, LLC.
33709  *
33710  * Originally Released Under LGPL - original licence link has changed is not relivant.
33711  *
33712  * Fork - LGPL
33713  * <script type="text/javascript">
33714  */
33715  
33716 /**
33717  * @class Roo.tree.AsyncTreeNode
33718  * @extends Roo.tree.TreeNode
33719  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33720  * @constructor
33721  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33722  */
33723  Roo.tree.AsyncTreeNode = function(config){
33724     this.loaded = false;
33725     this.loading = false;
33726     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33727     /**
33728     * @event beforeload
33729     * Fires before this node is loaded, return false to cancel
33730     * @param {Node} this This node
33731     */
33732     this.addEvents({'beforeload':true, 'load': true});
33733     /**
33734     * @event load
33735     * Fires when this node is loaded
33736     * @param {Node} this This node
33737     */
33738     /**
33739      * The loader used by this node (defaults to using the tree's defined loader)
33740      * @type TreeLoader
33741      * @property loader
33742      */
33743 };
33744 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33745     expand : function(deep, anim, callback){
33746         if(this.loading){ // if an async load is already running, waiting til it's done
33747             var timer;
33748             var f = function(){
33749                 if(!this.loading){ // done loading
33750                     clearInterval(timer);
33751                     this.expand(deep, anim, callback);
33752                 }
33753             }.createDelegate(this);
33754             timer = setInterval(f, 200);
33755             return;
33756         }
33757         if(!this.loaded){
33758             if(this.fireEvent("beforeload", this) === false){
33759                 return;
33760             }
33761             this.loading = true;
33762             this.ui.beforeLoad(this);
33763             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33764             if(loader){
33765                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33766                 return;
33767             }
33768         }
33769         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33770     },
33771     
33772     /**
33773      * Returns true if this node is currently loading
33774      * @return {Boolean}
33775      */
33776     isLoading : function(){
33777         return this.loading;  
33778     },
33779     
33780     loadComplete : function(deep, anim, callback){
33781         this.loading = false;
33782         this.loaded = true;
33783         this.ui.afterLoad(this);
33784         this.fireEvent("load", this);
33785         this.expand(deep, anim, callback);
33786     },
33787     
33788     /**
33789      * Returns true if this node has been loaded
33790      * @return {Boolean}
33791      */
33792     isLoaded : function(){
33793         return this.loaded;
33794     },
33795     
33796     hasChildNodes : function(){
33797         if(!this.isLeaf() && !this.loaded){
33798             return true;
33799         }else{
33800             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33801         }
33802     },
33803
33804     /**
33805      * Trigger a reload for this node
33806      * @param {Function} callback
33807      */
33808     reload : function(callback){
33809         this.collapse(false, false);
33810         while(this.firstChild){
33811             this.removeChild(this.firstChild);
33812         }
33813         this.childrenRendered = false;
33814         this.loaded = false;
33815         if(this.isHiddenRoot()){
33816             this.expanded = false;
33817         }
33818         this.expand(false, false, callback);
33819     }
33820 });/*
33821  * Based on:
33822  * Ext JS Library 1.1.1
33823  * Copyright(c) 2006-2007, Ext JS, LLC.
33824  *
33825  * Originally Released Under LGPL - original licence link has changed is not relivant.
33826  *
33827  * Fork - LGPL
33828  * <script type="text/javascript">
33829  */
33830  
33831 /**
33832  * @class Roo.tree.TreeNodeUI
33833  * @constructor
33834  * @param {Object} node The node to render
33835  * The TreeNode UI implementation is separate from the
33836  * tree implementation. Unless you are customizing the tree UI,
33837  * you should never have to use this directly.
33838  */
33839 Roo.tree.TreeNodeUI = function(node){
33840     this.node = node;
33841     this.rendered = false;
33842     this.animating = false;
33843     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33844 };
33845
33846 Roo.tree.TreeNodeUI.prototype = {
33847     removeChild : function(node){
33848         if(this.rendered){
33849             this.ctNode.removeChild(node.ui.getEl());
33850         }
33851     },
33852
33853     beforeLoad : function(){
33854          this.addClass("x-tree-node-loading");
33855     },
33856
33857     afterLoad : function(){
33858          this.removeClass("x-tree-node-loading");
33859     },
33860
33861     onTextChange : function(node, text, oldText){
33862         if(this.rendered){
33863             this.textNode.innerHTML = text;
33864         }
33865     },
33866
33867     onDisableChange : function(node, state){
33868         this.disabled = state;
33869         if(state){
33870             this.addClass("x-tree-node-disabled");
33871         }else{
33872             this.removeClass("x-tree-node-disabled");
33873         }
33874     },
33875
33876     onSelectedChange : function(state){
33877         if(state){
33878             this.focus();
33879             this.addClass("x-tree-selected");
33880         }else{
33881             //this.blur();
33882             this.removeClass("x-tree-selected");
33883         }
33884     },
33885
33886     onMove : function(tree, node, oldParent, newParent, index, refNode){
33887         this.childIndent = null;
33888         if(this.rendered){
33889             var targetNode = newParent.ui.getContainer();
33890             if(!targetNode){//target not rendered
33891                 this.holder = document.createElement("div");
33892                 this.holder.appendChild(this.wrap);
33893                 return;
33894             }
33895             var insertBefore = refNode ? refNode.ui.getEl() : null;
33896             if(insertBefore){
33897                 targetNode.insertBefore(this.wrap, insertBefore);
33898             }else{
33899                 targetNode.appendChild(this.wrap);
33900             }
33901             this.node.renderIndent(true);
33902         }
33903     },
33904
33905     addClass : function(cls){
33906         if(this.elNode){
33907             Roo.fly(this.elNode).addClass(cls);
33908         }
33909     },
33910
33911     removeClass : function(cls){
33912         if(this.elNode){
33913             Roo.fly(this.elNode).removeClass(cls);
33914         }
33915     },
33916
33917     remove : function(){
33918         if(this.rendered){
33919             this.holder = document.createElement("div");
33920             this.holder.appendChild(this.wrap);
33921         }
33922     },
33923
33924     fireEvent : function(){
33925         return this.node.fireEvent.apply(this.node, arguments);
33926     },
33927
33928     initEvents : function(){
33929         this.node.on("move", this.onMove, this);
33930         var E = Roo.EventManager;
33931         var a = this.anchor;
33932
33933         var el = Roo.fly(a, '_treeui');
33934
33935         if(Roo.isOpera){ // opera render bug ignores the CSS
33936             el.setStyle("text-decoration", "none");
33937         }
33938
33939         el.on("click", this.onClick, this);
33940         el.on("dblclick", this.onDblClick, this);
33941
33942         if(this.checkbox){
33943             Roo.EventManager.on(this.checkbox,
33944                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33945         }
33946
33947         el.on("contextmenu", this.onContextMenu, this);
33948
33949         var icon = Roo.fly(this.iconNode);
33950         icon.on("click", this.onClick, this);
33951         icon.on("dblclick", this.onDblClick, this);
33952         icon.on("contextmenu", this.onContextMenu, this);
33953         E.on(this.ecNode, "click", this.ecClick, this, true);
33954
33955         if(this.node.disabled){
33956             this.addClass("x-tree-node-disabled");
33957         }
33958         if(this.node.hidden){
33959             this.addClass("x-tree-node-disabled");
33960         }
33961         var ot = this.node.getOwnerTree();
33962         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33963         if(dd && (!this.node.isRoot || ot.rootVisible)){
33964             Roo.dd.Registry.register(this.elNode, {
33965                 node: this.node,
33966                 handles: this.getDDHandles(),
33967                 isHandle: false
33968             });
33969         }
33970     },
33971
33972     getDDHandles : function(){
33973         return [this.iconNode, this.textNode];
33974     },
33975
33976     hide : function(){
33977         if(this.rendered){
33978             this.wrap.style.display = "none";
33979         }
33980     },
33981
33982     show : function(){
33983         if(this.rendered){
33984             this.wrap.style.display = "";
33985         }
33986     },
33987
33988     onContextMenu : function(e){
33989         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33990             e.preventDefault();
33991             this.focus();
33992             this.fireEvent("contextmenu", this.node, e);
33993         }
33994     },
33995
33996     onClick : function(e){
33997         if(this.dropping){
33998             e.stopEvent();
33999             return;
34000         }
34001         if(this.fireEvent("beforeclick", this.node, e) !== false){
34002             if(!this.disabled && this.node.attributes.href){
34003                 this.fireEvent("click", this.node, e);
34004                 return;
34005             }
34006             e.preventDefault();
34007             if(this.disabled){
34008                 return;
34009             }
34010
34011             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
34012                 this.node.toggle();
34013             }
34014
34015             this.fireEvent("click", this.node, e);
34016         }else{
34017             e.stopEvent();
34018         }
34019     },
34020
34021     onDblClick : function(e){
34022         e.preventDefault();
34023         if(this.disabled){
34024             return;
34025         }
34026         if(this.checkbox){
34027             this.toggleCheck();
34028         }
34029         if(!this.animating && this.node.hasChildNodes()){
34030             this.node.toggle();
34031         }
34032         this.fireEvent("dblclick", this.node, e);
34033     },
34034
34035     onCheckChange : function(){
34036         var checked = this.checkbox.checked;
34037         this.node.attributes.checked = checked;
34038         this.fireEvent('checkchange', this.node, checked);
34039     },
34040
34041     ecClick : function(e){
34042         if(!this.animating && this.node.hasChildNodes()){
34043             this.node.toggle();
34044         }
34045     },
34046
34047     startDrop : function(){
34048         this.dropping = true;
34049     },
34050
34051     // delayed drop so the click event doesn't get fired on a drop
34052     endDrop : function(){
34053        setTimeout(function(){
34054            this.dropping = false;
34055        }.createDelegate(this), 50);
34056     },
34057
34058     expand : function(){
34059         this.updateExpandIcon();
34060         this.ctNode.style.display = "";
34061     },
34062
34063     focus : function(){
34064         if(!this.node.preventHScroll){
34065             try{this.anchor.focus();
34066             }catch(e){}
34067         }else if(!Roo.isIE){
34068             try{
34069                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34070                 var l = noscroll.scrollLeft;
34071                 this.anchor.focus();
34072                 noscroll.scrollLeft = l;
34073             }catch(e){}
34074         }
34075     },
34076
34077     toggleCheck : function(value){
34078         var cb = this.checkbox;
34079         if(cb){
34080             cb.checked = (value === undefined ? !cb.checked : value);
34081         }
34082     },
34083
34084     blur : function(){
34085         try{
34086             this.anchor.blur();
34087         }catch(e){}
34088     },
34089
34090     animExpand : function(callback){
34091         var ct = Roo.get(this.ctNode);
34092         ct.stopFx();
34093         if(!this.node.hasChildNodes()){
34094             this.updateExpandIcon();
34095             this.ctNode.style.display = "";
34096             Roo.callback(callback);
34097             return;
34098         }
34099         this.animating = true;
34100         this.updateExpandIcon();
34101
34102         ct.slideIn('t', {
34103            callback : function(){
34104                this.animating = false;
34105                Roo.callback(callback);
34106             },
34107             scope: this,
34108             duration: this.node.ownerTree.duration || .25
34109         });
34110     },
34111
34112     highlight : function(){
34113         var tree = this.node.getOwnerTree();
34114         Roo.fly(this.wrap).highlight(
34115             tree.hlColor || "C3DAF9",
34116             {endColor: tree.hlBaseColor}
34117         );
34118     },
34119
34120     collapse : function(){
34121         this.updateExpandIcon();
34122         this.ctNode.style.display = "none";
34123     },
34124
34125     animCollapse : function(callback){
34126         var ct = Roo.get(this.ctNode);
34127         ct.enableDisplayMode('block');
34128         ct.stopFx();
34129
34130         this.animating = true;
34131         this.updateExpandIcon();
34132
34133         ct.slideOut('t', {
34134             callback : function(){
34135                this.animating = false;
34136                Roo.callback(callback);
34137             },
34138             scope: this,
34139             duration: this.node.ownerTree.duration || .25
34140         });
34141     },
34142
34143     getContainer : function(){
34144         return this.ctNode;
34145     },
34146
34147     getEl : function(){
34148         return this.wrap;
34149     },
34150
34151     appendDDGhost : function(ghostNode){
34152         ghostNode.appendChild(this.elNode.cloneNode(true));
34153     },
34154
34155     getDDRepairXY : function(){
34156         return Roo.lib.Dom.getXY(this.iconNode);
34157     },
34158
34159     onRender : function(){
34160         this.render();
34161     },
34162
34163     render : function(bulkRender){
34164         var n = this.node, a = n.attributes;
34165         var targetNode = n.parentNode ?
34166               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34167
34168         if(!this.rendered){
34169             this.rendered = true;
34170
34171             this.renderElements(n, a, targetNode, bulkRender);
34172
34173             if(a.qtip){
34174                if(this.textNode.setAttributeNS){
34175                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34176                    if(a.qtipTitle){
34177                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34178                    }
34179                }else{
34180                    this.textNode.setAttribute("ext:qtip", a.qtip);
34181                    if(a.qtipTitle){
34182                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34183                    }
34184                }
34185             }else if(a.qtipCfg){
34186                 a.qtipCfg.target = Roo.id(this.textNode);
34187                 Roo.QuickTips.register(a.qtipCfg);
34188             }
34189             this.initEvents();
34190             if(!this.node.expanded){
34191                 this.updateExpandIcon();
34192             }
34193         }else{
34194             if(bulkRender === true) {
34195                 targetNode.appendChild(this.wrap);
34196             }
34197         }
34198     },
34199
34200     renderElements : function(n, a, targetNode, bulkRender)
34201     {
34202         // add some indent caching, this helps performance when rendering a large tree
34203         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34204         var t = n.getOwnerTree();
34205         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34206         if (typeof(n.attributes.html) != 'undefined') {
34207             txt = n.attributes.html;
34208         }
34209         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34210         var cb = typeof a.checked == 'boolean';
34211         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34212         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34213             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34214             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34215             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34216             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34217             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34218              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34219                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34220             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34221             "</li>"];
34222
34223         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34224             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34225                                 n.nextSibling.ui.getEl(), buf.join(""));
34226         }else{
34227             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34228         }
34229
34230         this.elNode = this.wrap.childNodes[0];
34231         this.ctNode = this.wrap.childNodes[1];
34232         var cs = this.elNode.childNodes;
34233         this.indentNode = cs[0];
34234         this.ecNode = cs[1];
34235         this.iconNode = cs[2];
34236         var index = 3;
34237         if(cb){
34238             this.checkbox = cs[3];
34239             index++;
34240         }
34241         this.anchor = cs[index];
34242         this.textNode = cs[index].firstChild;
34243     },
34244
34245     getAnchor : function(){
34246         return this.anchor;
34247     },
34248
34249     getTextEl : function(){
34250         return this.textNode;
34251     },
34252
34253     getIconEl : function(){
34254         return this.iconNode;
34255     },
34256
34257     isChecked : function(){
34258         return this.checkbox ? this.checkbox.checked : false;
34259     },
34260
34261     updateExpandIcon : function(){
34262         if(this.rendered){
34263             var n = this.node, c1, c2;
34264             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34265             var hasChild = n.hasChildNodes();
34266             if(hasChild){
34267                 if(n.expanded){
34268                     cls += "-minus";
34269                     c1 = "x-tree-node-collapsed";
34270                     c2 = "x-tree-node-expanded";
34271                 }else{
34272                     cls += "-plus";
34273                     c1 = "x-tree-node-expanded";
34274                     c2 = "x-tree-node-collapsed";
34275                 }
34276                 if(this.wasLeaf){
34277                     this.removeClass("x-tree-node-leaf");
34278                     this.wasLeaf = false;
34279                 }
34280                 if(this.c1 != c1 || this.c2 != c2){
34281                     Roo.fly(this.elNode).replaceClass(c1, c2);
34282                     this.c1 = c1; this.c2 = c2;
34283                 }
34284             }else{
34285                 // this changes non-leafs into leafs if they have no children.
34286                 // it's not very rational behaviour..
34287                 
34288                 if(!this.wasLeaf && this.node.leaf){
34289                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34290                     delete this.c1;
34291                     delete this.c2;
34292                     this.wasLeaf = true;
34293                 }
34294             }
34295             var ecc = "x-tree-ec-icon "+cls;
34296             if(this.ecc != ecc){
34297                 this.ecNode.className = ecc;
34298                 this.ecc = ecc;
34299             }
34300         }
34301     },
34302
34303     getChildIndent : function(){
34304         if(!this.childIndent){
34305             var buf = [];
34306             var p = this.node;
34307             while(p){
34308                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34309                     if(!p.isLast()) {
34310                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34311                     } else {
34312                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34313                     }
34314                 }
34315                 p = p.parentNode;
34316             }
34317             this.childIndent = buf.join("");
34318         }
34319         return this.childIndent;
34320     },
34321
34322     renderIndent : function(){
34323         if(this.rendered){
34324             var indent = "";
34325             var p = this.node.parentNode;
34326             if(p){
34327                 indent = p.ui.getChildIndent();
34328             }
34329             if(this.indentMarkup != indent){ // don't rerender if not required
34330                 this.indentNode.innerHTML = indent;
34331                 this.indentMarkup = indent;
34332             }
34333             this.updateExpandIcon();
34334         }
34335     }
34336 };
34337
34338 Roo.tree.RootTreeNodeUI = function(){
34339     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34340 };
34341 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34342     render : function(){
34343         if(!this.rendered){
34344             var targetNode = this.node.ownerTree.innerCt.dom;
34345             this.node.expanded = true;
34346             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34347             this.wrap = this.ctNode = targetNode.firstChild;
34348         }
34349     },
34350     collapse : function(){
34351     },
34352     expand : function(){
34353     }
34354 });/*
34355  * Based on:
34356  * Ext JS Library 1.1.1
34357  * Copyright(c) 2006-2007, Ext JS, LLC.
34358  *
34359  * Originally Released Under LGPL - original licence link has changed is not relivant.
34360  *
34361  * Fork - LGPL
34362  * <script type="text/javascript">
34363  */
34364 /**
34365  * @class Roo.tree.TreeLoader
34366  * @extends Roo.util.Observable
34367  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34368  * nodes from a specified URL. The response must be a javascript Array definition
34369  * who's elements are node definition objects. eg:
34370  * <pre><code>
34371 {  success : true,
34372    data :      [
34373    
34374     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34375     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34376     ]
34377 }
34378
34379
34380 </code></pre>
34381  * <br><br>
34382  * The old style respose with just an array is still supported, but not recommended.
34383  * <br><br>
34384  *
34385  * A server request is sent, and child nodes are loaded only when a node is expanded.
34386  * The loading node's id is passed to the server under the parameter name "node" to
34387  * enable the server to produce the correct child nodes.
34388  * <br><br>
34389  * To pass extra parameters, an event handler may be attached to the "beforeload"
34390  * event, and the parameters specified in the TreeLoader's baseParams property:
34391  * <pre><code>
34392     myTreeLoader.on("beforeload", function(treeLoader, node) {
34393         this.baseParams.category = node.attributes.category;
34394     }, this);
34395 </code></pre><
34396  * This would pass an HTTP parameter called "category" to the server containing
34397  * the value of the Node's "category" attribute.
34398  * @constructor
34399  * Creates a new Treeloader.
34400  * @param {Object} config A config object containing config properties.
34401  */
34402 Roo.tree.TreeLoader = function(config){
34403     this.baseParams = {};
34404     this.requestMethod = "POST";
34405     Roo.apply(this, config);
34406
34407     this.addEvents({
34408     
34409         /**
34410          * @event beforeload
34411          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34412          * @param {Object} This TreeLoader object.
34413          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34414          * @param {Object} callback The callback function specified in the {@link #load} call.
34415          */
34416         beforeload : true,
34417         /**
34418          * @event load
34419          * Fires when the node has been successfuly loaded.
34420          * @param {Object} This TreeLoader object.
34421          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34422          * @param {Object} response The response object containing the data from the server.
34423          */
34424         load : true,
34425         /**
34426          * @event loadexception
34427          * Fires if the network request failed.
34428          * @param {Object} This TreeLoader object.
34429          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34430          * @param {Object} response The response object containing the data from the server.
34431          */
34432         loadexception : true,
34433         /**
34434          * @event create
34435          * Fires before a node is created, enabling you to return custom Node types 
34436          * @param {Object} This TreeLoader object.
34437          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34438          */
34439         create : true
34440     });
34441
34442     Roo.tree.TreeLoader.superclass.constructor.call(this);
34443 };
34444
34445 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34446     /**
34447     * @cfg {String} dataUrl The URL from which to request a Json string which
34448     * specifies an array of node definition object representing the child nodes
34449     * to be loaded.
34450     */
34451     /**
34452     * @cfg {String} requestMethod either GET or POST
34453     * defaults to POST (due to BC)
34454     * to be loaded.
34455     */
34456     /**
34457     * @cfg {Object} baseParams (optional) An object containing properties which
34458     * specify HTTP parameters to be passed to each request for child nodes.
34459     */
34460     /**
34461     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34462     * created by this loader. If the attributes sent by the server have an attribute in this object,
34463     * they take priority.
34464     */
34465     /**
34466     * @cfg {Object} uiProviders (optional) An object containing properties which
34467     * 
34468     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34469     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34470     * <i>uiProvider</i> attribute of a returned child node is a string rather
34471     * than a reference to a TreeNodeUI implementation, this that string value
34472     * is used as a property name in the uiProviders object. You can define the provider named
34473     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34474     */
34475     uiProviders : {},
34476
34477     /**
34478     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34479     * child nodes before loading.
34480     */
34481     clearOnLoad : true,
34482
34483     /**
34484     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34485     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34486     * Grid query { data : [ .....] }
34487     */
34488     
34489     root : false,
34490      /**
34491     * @cfg {String} queryParam (optional) 
34492     * Name of the query as it will be passed on the querystring (defaults to 'node')
34493     * eg. the request will be ?node=[id]
34494     */
34495     
34496     
34497     queryParam: false,
34498     
34499     /**
34500      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34501      * This is called automatically when a node is expanded, but may be used to reload
34502      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34503      * @param {Roo.tree.TreeNode} node
34504      * @param {Function} callback
34505      */
34506     load : function(node, callback){
34507         if(this.clearOnLoad){
34508             while(node.firstChild){
34509                 node.removeChild(node.firstChild);
34510             }
34511         }
34512         if(node.attributes.children){ // preloaded json children
34513             var cs = node.attributes.children;
34514             for(var i = 0, len = cs.length; i < len; i++){
34515                 node.appendChild(this.createNode(cs[i]));
34516             }
34517             if(typeof callback == "function"){
34518                 callback();
34519             }
34520         }else if(this.dataUrl){
34521             this.requestData(node, callback);
34522         }
34523     },
34524
34525     getParams: function(node){
34526         var buf = [], bp = this.baseParams;
34527         for(var key in bp){
34528             if(typeof bp[key] != "function"){
34529                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34530             }
34531         }
34532         var n = this.queryParam === false ? 'node' : this.queryParam;
34533         buf.push(n + "=", encodeURIComponent(node.id));
34534         return buf.join("");
34535     },
34536
34537     requestData : function(node, callback){
34538         if(this.fireEvent("beforeload", this, node, callback) !== false){
34539             this.transId = Roo.Ajax.request({
34540                 method:this.requestMethod,
34541                 url: this.dataUrl||this.url,
34542                 success: this.handleResponse,
34543                 failure: this.handleFailure,
34544                 scope: this,
34545                 argument: {callback: callback, node: node},
34546                 params: this.getParams(node)
34547             });
34548         }else{
34549             // if the load is cancelled, make sure we notify
34550             // the node that we are done
34551             if(typeof callback == "function"){
34552                 callback();
34553             }
34554         }
34555     },
34556
34557     isLoading : function(){
34558         return this.transId ? true : false;
34559     },
34560
34561     abort : function(){
34562         if(this.isLoading()){
34563             Roo.Ajax.abort(this.transId);
34564         }
34565     },
34566
34567     // private
34568     createNode : function(attr)
34569     {
34570         // apply baseAttrs, nice idea Corey!
34571         if(this.baseAttrs){
34572             Roo.applyIf(attr, this.baseAttrs);
34573         }
34574         if(this.applyLoader !== false){
34575             attr.loader = this;
34576         }
34577         // uiProvider = depreciated..
34578         
34579         if(typeof(attr.uiProvider) == 'string'){
34580            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34581                 /**  eval:var:attr */ eval(attr.uiProvider);
34582         }
34583         if(typeof(this.uiProviders['default']) != 'undefined') {
34584             attr.uiProvider = this.uiProviders['default'];
34585         }
34586         
34587         this.fireEvent('create', this, attr);
34588         
34589         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34590         return(attr.leaf ?
34591                         new Roo.tree.TreeNode(attr) :
34592                         new Roo.tree.AsyncTreeNode(attr));
34593     },
34594
34595     processResponse : function(response, node, callback)
34596     {
34597         var json = response.responseText;
34598         try {
34599             
34600             var o = Roo.decode(json);
34601             
34602             if (this.root === false && typeof(o.success) != undefined) {
34603                 this.root = 'data'; // the default behaviour for list like data..
34604                 }
34605                 
34606             if (this.root !== false &&  !o.success) {
34607                 // it's a failure condition.
34608                 var a = response.argument;
34609                 this.fireEvent("loadexception", this, a.node, response);
34610                 Roo.log("Load failed - should have a handler really");
34611                 return;
34612             }
34613             
34614             
34615             
34616             if (this.root !== false) {
34617                  o = o[this.root];
34618             }
34619             
34620             for(var i = 0, len = o.length; i < len; i++){
34621                 var n = this.createNode(o[i]);
34622                 if(n){
34623                     node.appendChild(n);
34624                 }
34625             }
34626             if(typeof callback == "function"){
34627                 callback(this, node);
34628             }
34629         }catch(e){
34630             this.handleFailure(response);
34631         }
34632     },
34633
34634     handleResponse : function(response){
34635         this.transId = false;
34636         var a = response.argument;
34637         this.processResponse(response, a.node, a.callback);
34638         this.fireEvent("load", this, a.node, response);
34639     },
34640
34641     handleFailure : function(response)
34642     {
34643         // should handle failure better..
34644         this.transId = false;
34645         var a = response.argument;
34646         this.fireEvent("loadexception", this, a.node, response);
34647         if(typeof a.callback == "function"){
34648             a.callback(this, a.node);
34649         }
34650     }
34651 });/*
34652  * Based on:
34653  * Ext JS Library 1.1.1
34654  * Copyright(c) 2006-2007, Ext JS, LLC.
34655  *
34656  * Originally Released Under LGPL - original licence link has changed is not relivant.
34657  *
34658  * Fork - LGPL
34659  * <script type="text/javascript">
34660  */
34661
34662 /**
34663 * @class Roo.tree.TreeFilter
34664 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34665 * @param {TreePanel} tree
34666 * @param {Object} config (optional)
34667  */
34668 Roo.tree.TreeFilter = function(tree, config){
34669     this.tree = tree;
34670     this.filtered = {};
34671     Roo.apply(this, config);
34672 };
34673
34674 Roo.tree.TreeFilter.prototype = {
34675     clearBlank:false,
34676     reverse:false,
34677     autoClear:false,
34678     remove:false,
34679
34680      /**
34681      * Filter the data by a specific attribute.
34682      * @param {String/RegExp} value Either string that the attribute value
34683      * should start with or a RegExp to test against the attribute
34684      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34685      * @param {TreeNode} startNode (optional) The node to start the filter at.
34686      */
34687     filter : function(value, attr, startNode){
34688         attr = attr || "text";
34689         var f;
34690         if(typeof value == "string"){
34691             var vlen = value.length;
34692             // auto clear empty filter
34693             if(vlen == 0 && this.clearBlank){
34694                 this.clear();
34695                 return;
34696             }
34697             value = value.toLowerCase();
34698             f = function(n){
34699                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34700             };
34701         }else if(value.exec){ // regex?
34702             f = function(n){
34703                 return value.test(n.attributes[attr]);
34704             };
34705         }else{
34706             throw 'Illegal filter type, must be string or regex';
34707         }
34708         this.filterBy(f, null, startNode);
34709         },
34710
34711     /**
34712      * Filter by a function. The passed function will be called with each
34713      * node in the tree (or from the startNode). If the function returns true, the node is kept
34714      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34715      * @param {Function} fn The filter function
34716      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34717      */
34718     filterBy : function(fn, scope, startNode){
34719         startNode = startNode || this.tree.root;
34720         if(this.autoClear){
34721             this.clear();
34722         }
34723         var af = this.filtered, rv = this.reverse;
34724         var f = function(n){
34725             if(n == startNode){
34726                 return true;
34727             }
34728             if(af[n.id]){
34729                 return false;
34730             }
34731             var m = fn.call(scope || n, n);
34732             if(!m || rv){
34733                 af[n.id] = n;
34734                 n.ui.hide();
34735                 return false;
34736             }
34737             return true;
34738         };
34739         startNode.cascade(f);
34740         if(this.remove){
34741            for(var id in af){
34742                if(typeof id != "function"){
34743                    var n = af[id];
34744                    if(n && n.parentNode){
34745                        n.parentNode.removeChild(n);
34746                    }
34747                }
34748            }
34749         }
34750     },
34751
34752     /**
34753      * Clears the current filter. Note: with the "remove" option
34754      * set a filter cannot be cleared.
34755      */
34756     clear : function(){
34757         var t = this.tree;
34758         var af = this.filtered;
34759         for(var id in af){
34760             if(typeof id != "function"){
34761                 var n = af[id];
34762                 if(n){
34763                     n.ui.show();
34764                 }
34765             }
34766         }
34767         this.filtered = {};
34768     }
34769 };
34770 /*
34771  * Based on:
34772  * Ext JS Library 1.1.1
34773  * Copyright(c) 2006-2007, Ext JS, LLC.
34774  *
34775  * Originally Released Under LGPL - original licence link has changed is not relivant.
34776  *
34777  * Fork - LGPL
34778  * <script type="text/javascript">
34779  */
34780  
34781
34782 /**
34783  * @class Roo.tree.TreeSorter
34784  * Provides sorting of nodes in a TreePanel
34785  * 
34786  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34787  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34788  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34789  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34790  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34791  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34792  * @constructor
34793  * @param {TreePanel} tree
34794  * @param {Object} config
34795  */
34796 Roo.tree.TreeSorter = function(tree, config){
34797     Roo.apply(this, config);
34798     tree.on("beforechildrenrendered", this.doSort, this);
34799     tree.on("append", this.updateSort, this);
34800     tree.on("insert", this.updateSort, this);
34801     
34802     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34803     var p = this.property || "text";
34804     var sortType = this.sortType;
34805     var fs = this.folderSort;
34806     var cs = this.caseSensitive === true;
34807     var leafAttr = this.leafAttr || 'leaf';
34808
34809     this.sortFn = function(n1, n2){
34810         if(fs){
34811             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34812                 return 1;
34813             }
34814             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34815                 return -1;
34816             }
34817         }
34818         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34819         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34820         if(v1 < v2){
34821                         return dsc ? +1 : -1;
34822                 }else if(v1 > v2){
34823                         return dsc ? -1 : +1;
34824         }else{
34825                 return 0;
34826         }
34827     };
34828 };
34829
34830 Roo.tree.TreeSorter.prototype = {
34831     doSort : function(node){
34832         node.sort(this.sortFn);
34833     },
34834     
34835     compareNodes : function(n1, n2){
34836         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34837     },
34838     
34839     updateSort : function(tree, node){
34840         if(node.childrenRendered){
34841             this.doSort.defer(1, this, [node]);
34842         }
34843     }
34844 };/*
34845  * Based on:
34846  * Ext JS Library 1.1.1
34847  * Copyright(c) 2006-2007, Ext JS, LLC.
34848  *
34849  * Originally Released Under LGPL - original licence link has changed is not relivant.
34850  *
34851  * Fork - LGPL
34852  * <script type="text/javascript">
34853  */
34854
34855 if(Roo.dd.DropZone){
34856     
34857 Roo.tree.TreeDropZone = function(tree, config){
34858     this.allowParentInsert = false;
34859     this.allowContainerDrop = false;
34860     this.appendOnly = false;
34861     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34862     this.tree = tree;
34863     this.lastInsertClass = "x-tree-no-status";
34864     this.dragOverData = {};
34865 };
34866
34867 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34868     ddGroup : "TreeDD",
34869     scroll:  true,
34870     
34871     expandDelay : 1000,
34872     
34873     expandNode : function(node){
34874         if(node.hasChildNodes() && !node.isExpanded()){
34875             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34876         }
34877     },
34878     
34879     queueExpand : function(node){
34880         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34881     },
34882     
34883     cancelExpand : function(){
34884         if(this.expandProcId){
34885             clearTimeout(this.expandProcId);
34886             this.expandProcId = false;
34887         }
34888     },
34889     
34890     isValidDropPoint : function(n, pt, dd, e, data){
34891         if(!n || !data){ return false; }
34892         var targetNode = n.node;
34893         var dropNode = data.node;
34894         // default drop rules
34895         if(!(targetNode && targetNode.isTarget && pt)){
34896             return false;
34897         }
34898         if(pt == "append" && targetNode.allowChildren === false){
34899             return false;
34900         }
34901         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34902             return false;
34903         }
34904         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34905             return false;
34906         }
34907         // reuse the object
34908         var overEvent = this.dragOverData;
34909         overEvent.tree = this.tree;
34910         overEvent.target = targetNode;
34911         overEvent.data = data;
34912         overEvent.point = pt;
34913         overEvent.source = dd;
34914         overEvent.rawEvent = e;
34915         overEvent.dropNode = dropNode;
34916         overEvent.cancel = false;  
34917         var result = this.tree.fireEvent("nodedragover", overEvent);
34918         return overEvent.cancel === false && result !== false;
34919     },
34920     
34921     getDropPoint : function(e, n, dd)
34922     {
34923         var tn = n.node;
34924         if(tn.isRoot){
34925             return tn.allowChildren !== false ? "append" : false; // always append for root
34926         }
34927         var dragEl = n.ddel;
34928         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34929         var y = Roo.lib.Event.getPageY(e);
34930         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34931         
34932         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34933         var noAppend = tn.allowChildren === false;
34934         if(this.appendOnly || tn.parentNode.allowChildren === false){
34935             return noAppend ? false : "append";
34936         }
34937         var noBelow = false;
34938         if(!this.allowParentInsert){
34939             noBelow = tn.hasChildNodes() && tn.isExpanded();
34940         }
34941         var q = (b - t) / (noAppend ? 2 : 3);
34942         if(y >= t && y < (t + q)){
34943             return "above";
34944         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34945             return "below";
34946         }else{
34947             return "append";
34948         }
34949     },
34950     
34951     onNodeEnter : function(n, dd, e, data)
34952     {
34953         this.cancelExpand();
34954     },
34955     
34956     onNodeOver : function(n, dd, e, data)
34957     {
34958        
34959         var pt = this.getDropPoint(e, n, dd);
34960         var node = n.node;
34961         
34962         // auto node expand check
34963         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34964             this.queueExpand(node);
34965         }else if(pt != "append"){
34966             this.cancelExpand();
34967         }
34968         
34969         // set the insert point style on the target node
34970         var returnCls = this.dropNotAllowed;
34971         if(this.isValidDropPoint(n, pt, dd, e, data)){
34972            if(pt){
34973                var el = n.ddel;
34974                var cls;
34975                if(pt == "above"){
34976                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34977                    cls = "x-tree-drag-insert-above";
34978                }else if(pt == "below"){
34979                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34980                    cls = "x-tree-drag-insert-below";
34981                }else{
34982                    returnCls = "x-tree-drop-ok-append";
34983                    cls = "x-tree-drag-append";
34984                }
34985                if(this.lastInsertClass != cls){
34986                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34987                    this.lastInsertClass = cls;
34988                }
34989            }
34990        }
34991        return returnCls;
34992     },
34993     
34994     onNodeOut : function(n, dd, e, data){
34995         
34996         this.cancelExpand();
34997         this.removeDropIndicators(n);
34998     },
34999     
35000     onNodeDrop : function(n, dd, e, data){
35001         var point = this.getDropPoint(e, n, dd);
35002         var targetNode = n.node;
35003         targetNode.ui.startDrop();
35004         if(!this.isValidDropPoint(n, point, dd, e, data)){
35005             targetNode.ui.endDrop();
35006             return false;
35007         }
35008         // first try to find the drop node
35009         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
35010         var dropEvent = {
35011             tree : this.tree,
35012             target: targetNode,
35013             data: data,
35014             point: point,
35015             source: dd,
35016             rawEvent: e,
35017             dropNode: dropNode,
35018             cancel: !dropNode   
35019         };
35020         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
35021         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
35022             targetNode.ui.endDrop();
35023             return false;
35024         }
35025         // allow target changing
35026         targetNode = dropEvent.target;
35027         if(point == "append" && !targetNode.isExpanded()){
35028             targetNode.expand(false, null, function(){
35029                 this.completeDrop(dropEvent);
35030             }.createDelegate(this));
35031         }else{
35032             this.completeDrop(dropEvent);
35033         }
35034         return true;
35035     },
35036     
35037     completeDrop : function(de){
35038         var ns = de.dropNode, p = de.point, t = de.target;
35039         if(!(ns instanceof Array)){
35040             ns = [ns];
35041         }
35042         var n;
35043         for(var i = 0, len = ns.length; i < len; i++){
35044             n = ns[i];
35045             if(p == "above"){
35046                 t.parentNode.insertBefore(n, t);
35047             }else if(p == "below"){
35048                 t.parentNode.insertBefore(n, t.nextSibling);
35049             }else{
35050                 t.appendChild(n);
35051             }
35052         }
35053         n.ui.focus();
35054         if(this.tree.hlDrop){
35055             n.ui.highlight();
35056         }
35057         t.ui.endDrop();
35058         this.tree.fireEvent("nodedrop", de);
35059     },
35060     
35061     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35062         if(this.tree.hlDrop){
35063             dropNode.ui.focus();
35064             dropNode.ui.highlight();
35065         }
35066         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35067     },
35068     
35069     getTree : function(){
35070         return this.tree;
35071     },
35072     
35073     removeDropIndicators : function(n){
35074         if(n && n.ddel){
35075             var el = n.ddel;
35076             Roo.fly(el).removeClass([
35077                     "x-tree-drag-insert-above",
35078                     "x-tree-drag-insert-below",
35079                     "x-tree-drag-append"]);
35080             this.lastInsertClass = "_noclass";
35081         }
35082     },
35083     
35084     beforeDragDrop : function(target, e, id){
35085         this.cancelExpand();
35086         return true;
35087     },
35088     
35089     afterRepair : function(data){
35090         if(data && Roo.enableFx){
35091             data.node.ui.highlight();
35092         }
35093         this.hideProxy();
35094     } 
35095     
35096 });
35097
35098 }
35099 /*
35100  * Based on:
35101  * Ext JS Library 1.1.1
35102  * Copyright(c) 2006-2007, Ext JS, LLC.
35103  *
35104  * Originally Released Under LGPL - original licence link has changed is not relivant.
35105  *
35106  * Fork - LGPL
35107  * <script type="text/javascript">
35108  */
35109  
35110
35111 if(Roo.dd.DragZone){
35112 Roo.tree.TreeDragZone = function(tree, config){
35113     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35114     this.tree = tree;
35115 };
35116
35117 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35118     ddGroup : "TreeDD",
35119    
35120     onBeforeDrag : function(data, e){
35121         var n = data.node;
35122         return n && n.draggable && !n.disabled;
35123     },
35124      
35125     
35126     onInitDrag : function(e){
35127         var data = this.dragData;
35128         this.tree.getSelectionModel().select(data.node);
35129         this.proxy.update("");
35130         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35131         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35132     },
35133     
35134     getRepairXY : function(e, data){
35135         return data.node.ui.getDDRepairXY();
35136     },
35137     
35138     onEndDrag : function(data, e){
35139         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35140         
35141         
35142     },
35143     
35144     onValidDrop : function(dd, e, id){
35145         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35146         this.hideProxy();
35147     },
35148     
35149     beforeInvalidDrop : function(e, id){
35150         // this scrolls the original position back into view
35151         var sm = this.tree.getSelectionModel();
35152         sm.clearSelections();
35153         sm.select(this.dragData.node);
35154     }
35155 });
35156 }/*
35157  * Based on:
35158  * Ext JS Library 1.1.1
35159  * Copyright(c) 2006-2007, Ext JS, LLC.
35160  *
35161  * Originally Released Under LGPL - original licence link has changed is not relivant.
35162  *
35163  * Fork - LGPL
35164  * <script type="text/javascript">
35165  */
35166 /**
35167  * @class Roo.tree.TreeEditor
35168  * @extends Roo.Editor
35169  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35170  * as the editor field.
35171  * @constructor
35172  * @param {Object} config (used to be the tree panel.)
35173  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35174  * 
35175  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35176  * @cfg {Roo.form.TextField|Object} field The field configuration
35177  *
35178  * 
35179  */
35180 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35181     var tree = config;
35182     var field;
35183     if (oldconfig) { // old style..
35184         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35185     } else {
35186         // new style..
35187         tree = config.tree;
35188         config.field = config.field  || {};
35189         config.field.xtype = 'TextField';
35190         field = Roo.factory(config.field, Roo.form);
35191     }
35192     config = config || {};
35193     
35194     
35195     this.addEvents({
35196         /**
35197          * @event beforenodeedit
35198          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35199          * false from the handler of this event.
35200          * @param {Editor} this
35201          * @param {Roo.tree.Node} node 
35202          */
35203         "beforenodeedit" : true
35204     });
35205     
35206     //Roo.log(config);
35207     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35208
35209     this.tree = tree;
35210
35211     tree.on('beforeclick', this.beforeNodeClick, this);
35212     tree.getTreeEl().on('mousedown', this.hide, this);
35213     this.on('complete', this.updateNode, this);
35214     this.on('beforestartedit', this.fitToTree, this);
35215     this.on('startedit', this.bindScroll, this, {delay:10});
35216     this.on('specialkey', this.onSpecialKey, this);
35217 };
35218
35219 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35220     /**
35221      * @cfg {String} alignment
35222      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35223      */
35224     alignment: "l-l",
35225     // inherit
35226     autoSize: false,
35227     /**
35228      * @cfg {Boolean} hideEl
35229      * True to hide the bound element while the editor is displayed (defaults to false)
35230      */
35231     hideEl : false,
35232     /**
35233      * @cfg {String} cls
35234      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35235      */
35236     cls: "x-small-editor x-tree-editor",
35237     /**
35238      * @cfg {Boolean} shim
35239      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35240      */
35241     shim:false,
35242     // inherit
35243     shadow:"frame",
35244     /**
35245      * @cfg {Number} maxWidth
35246      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35247      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35248      * scroll and client offsets into account prior to each edit.
35249      */
35250     maxWidth: 250,
35251
35252     editDelay : 350,
35253
35254     // private
35255     fitToTree : function(ed, el){
35256         var td = this.tree.getTreeEl().dom, nd = el.dom;
35257         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35258             td.scrollLeft = nd.offsetLeft;
35259         }
35260         var w = Math.min(
35261                 this.maxWidth,
35262                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35263         this.setSize(w, '');
35264         
35265         return this.fireEvent('beforenodeedit', this, this.editNode);
35266         
35267     },
35268
35269     // private
35270     triggerEdit : function(node){
35271         this.completeEdit();
35272         this.editNode = node;
35273         this.startEdit(node.ui.textNode, node.text);
35274     },
35275
35276     // private
35277     bindScroll : function(){
35278         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35279     },
35280
35281     // private
35282     beforeNodeClick : function(node, e){
35283         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35284         this.lastClick = new Date();
35285         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35286             e.stopEvent();
35287             this.triggerEdit(node);
35288             return false;
35289         }
35290         return true;
35291     },
35292
35293     // private
35294     updateNode : function(ed, value){
35295         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35296         this.editNode.setText(value);
35297     },
35298
35299     // private
35300     onHide : function(){
35301         Roo.tree.TreeEditor.superclass.onHide.call(this);
35302         if(this.editNode){
35303             this.editNode.ui.focus();
35304         }
35305     },
35306
35307     // private
35308     onSpecialKey : function(field, e){
35309         var k = e.getKey();
35310         if(k == e.ESC){
35311             e.stopEvent();
35312             this.cancelEdit();
35313         }else if(k == e.ENTER && !e.hasModifier()){
35314             e.stopEvent();
35315             this.completeEdit();
35316         }
35317     }
35318 });//<Script type="text/javascript">
35319 /*
35320  * Based on:
35321  * Ext JS Library 1.1.1
35322  * Copyright(c) 2006-2007, Ext JS, LLC.
35323  *
35324  * Originally Released Under LGPL - original licence link has changed is not relivant.
35325  *
35326  * Fork - LGPL
35327  * <script type="text/javascript">
35328  */
35329  
35330 /**
35331  * Not documented??? - probably should be...
35332  */
35333
35334 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35335     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35336     
35337     renderElements : function(n, a, targetNode, bulkRender){
35338         //consel.log("renderElements?");
35339         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35340
35341         var t = n.getOwnerTree();
35342         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35343         
35344         var cols = t.columns;
35345         var bw = t.borderWidth;
35346         var c = cols[0];
35347         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35348          var cb = typeof a.checked == "boolean";
35349         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35350         var colcls = 'x-t-' + tid + '-c0';
35351         var buf = [
35352             '<li class="x-tree-node">',
35353             
35354                 
35355                 '<div class="x-tree-node-el ', a.cls,'">',
35356                     // extran...
35357                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35358                 
35359                 
35360                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35361                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35362                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35363                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35364                            (a.iconCls ? ' '+a.iconCls : ''),
35365                            '" unselectable="on" />',
35366                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35367                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35368                              
35369                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35370                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35371                             '<span unselectable="on" qtip="' + tx + '">',
35372                              tx,
35373                              '</span></a>' ,
35374                     '</div>',
35375                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35376                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35377                  ];
35378         for(var i = 1, len = cols.length; i < len; i++){
35379             c = cols[i];
35380             colcls = 'x-t-' + tid + '-c' +i;
35381             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35382             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35383                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35384                       "</div>");
35385          }
35386          
35387          buf.push(
35388             '</a>',
35389             '<div class="x-clear"></div></div>',
35390             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35391             "</li>");
35392         
35393         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35394             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35395                                 n.nextSibling.ui.getEl(), buf.join(""));
35396         }else{
35397             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35398         }
35399         var el = this.wrap.firstChild;
35400         this.elRow = el;
35401         this.elNode = el.firstChild;
35402         this.ranchor = el.childNodes[1];
35403         this.ctNode = this.wrap.childNodes[1];
35404         var cs = el.firstChild.childNodes;
35405         this.indentNode = cs[0];
35406         this.ecNode = cs[1];
35407         this.iconNode = cs[2];
35408         var index = 3;
35409         if(cb){
35410             this.checkbox = cs[3];
35411             index++;
35412         }
35413         this.anchor = cs[index];
35414         
35415         this.textNode = cs[index].firstChild;
35416         
35417         //el.on("click", this.onClick, this);
35418         //el.on("dblclick", this.onDblClick, this);
35419         
35420         
35421        // console.log(this);
35422     },
35423     initEvents : function(){
35424         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35425         
35426             
35427         var a = this.ranchor;
35428
35429         var el = Roo.get(a);
35430
35431         if(Roo.isOpera){ // opera render bug ignores the CSS
35432             el.setStyle("text-decoration", "none");
35433         }
35434
35435         el.on("click", this.onClick, this);
35436         el.on("dblclick", this.onDblClick, this);
35437         el.on("contextmenu", this.onContextMenu, this);
35438         
35439     },
35440     
35441     /*onSelectedChange : function(state){
35442         if(state){
35443             this.focus();
35444             this.addClass("x-tree-selected");
35445         }else{
35446             //this.blur();
35447             this.removeClass("x-tree-selected");
35448         }
35449     },*/
35450     addClass : function(cls){
35451         if(this.elRow){
35452             Roo.fly(this.elRow).addClass(cls);
35453         }
35454         
35455     },
35456     
35457     
35458     removeClass : function(cls){
35459         if(this.elRow){
35460             Roo.fly(this.elRow).removeClass(cls);
35461         }
35462     }
35463
35464     
35465     
35466 });//<Script type="text/javascript">
35467
35468 /*
35469  * Based on:
35470  * Ext JS Library 1.1.1
35471  * Copyright(c) 2006-2007, Ext JS, LLC.
35472  *
35473  * Originally Released Under LGPL - original licence link has changed is not relivant.
35474  *
35475  * Fork - LGPL
35476  * <script type="text/javascript">
35477  */
35478  
35479
35480 /**
35481  * @class Roo.tree.ColumnTree
35482  * @extends Roo.data.TreePanel
35483  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35484  * @cfg {int} borderWidth  compined right/left border allowance
35485  * @constructor
35486  * @param {String/HTMLElement/Element} el The container element
35487  * @param {Object} config
35488  */
35489 Roo.tree.ColumnTree =  function(el, config)
35490 {
35491    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35492    this.addEvents({
35493         /**
35494         * @event resize
35495         * Fire this event on a container when it resizes
35496         * @param {int} w Width
35497         * @param {int} h Height
35498         */
35499        "resize" : true
35500     });
35501     this.on('resize', this.onResize, this);
35502 };
35503
35504 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35505     //lines:false,
35506     
35507     
35508     borderWidth: Roo.isBorderBox ? 0 : 2, 
35509     headEls : false,
35510     
35511     render : function(){
35512         // add the header.....
35513        
35514         Roo.tree.ColumnTree.superclass.render.apply(this);
35515         
35516         this.el.addClass('x-column-tree');
35517         
35518         this.headers = this.el.createChild(
35519             {cls:'x-tree-headers'},this.innerCt.dom);
35520    
35521         var cols = this.columns, c;
35522         var totalWidth = 0;
35523         this.headEls = [];
35524         var  len = cols.length;
35525         for(var i = 0; i < len; i++){
35526              c = cols[i];
35527              totalWidth += c.width;
35528             this.headEls.push(this.headers.createChild({
35529                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35530                  cn: {
35531                      cls:'x-tree-hd-text',
35532                      html: c.header
35533                  },
35534                  style:'width:'+(c.width-this.borderWidth)+'px;'
35535              }));
35536         }
35537         this.headers.createChild({cls:'x-clear'});
35538         // prevent floats from wrapping when clipped
35539         this.headers.setWidth(totalWidth);
35540         //this.innerCt.setWidth(totalWidth);
35541         this.innerCt.setStyle({ overflow: 'auto' });
35542         this.onResize(this.width, this.height);
35543              
35544         
35545     },
35546     onResize : function(w,h)
35547     {
35548         this.height = h;
35549         this.width = w;
35550         // resize cols..
35551         this.innerCt.setWidth(this.width);
35552         this.innerCt.setHeight(this.height-20);
35553         
35554         // headers...
35555         var cols = this.columns, c;
35556         var totalWidth = 0;
35557         var expEl = false;
35558         var len = cols.length;
35559         for(var i = 0; i < len; i++){
35560             c = cols[i];
35561             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35562                 // it's the expander..
35563                 expEl  = this.headEls[i];
35564                 continue;
35565             }
35566             totalWidth += c.width;
35567             
35568         }
35569         if (expEl) {
35570             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35571         }
35572         this.headers.setWidth(w-20);
35573
35574         
35575         
35576         
35577     }
35578 });
35579 /*
35580  * Based on:
35581  * Ext JS Library 1.1.1
35582  * Copyright(c) 2006-2007, Ext JS, LLC.
35583  *
35584  * Originally Released Under LGPL - original licence link has changed is not relivant.
35585  *
35586  * Fork - LGPL
35587  * <script type="text/javascript">
35588  */
35589  
35590 /**
35591  * @class Roo.menu.Menu
35592  * @extends Roo.util.Observable
35593  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35594  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35595  * @constructor
35596  * Creates a new Menu
35597  * @param {Object} config Configuration options
35598  */
35599 Roo.menu.Menu = function(config){
35600     Roo.apply(this, config);
35601     this.id = this.id || Roo.id();
35602     this.addEvents({
35603         /**
35604          * @event beforeshow
35605          * Fires before this menu is displayed
35606          * @param {Roo.menu.Menu} this
35607          */
35608         beforeshow : true,
35609         /**
35610          * @event beforehide
35611          * Fires before this menu is hidden
35612          * @param {Roo.menu.Menu} this
35613          */
35614         beforehide : true,
35615         /**
35616          * @event show
35617          * Fires after this menu is displayed
35618          * @param {Roo.menu.Menu} this
35619          */
35620         show : true,
35621         /**
35622          * @event hide
35623          * Fires after this menu is hidden
35624          * @param {Roo.menu.Menu} this
35625          */
35626         hide : true,
35627         /**
35628          * @event click
35629          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35630          * @param {Roo.menu.Menu} this
35631          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35632          * @param {Roo.EventObject} e
35633          */
35634         click : true,
35635         /**
35636          * @event mouseover
35637          * Fires when the mouse is hovering over this menu
35638          * @param {Roo.menu.Menu} this
35639          * @param {Roo.EventObject} e
35640          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35641          */
35642         mouseover : true,
35643         /**
35644          * @event mouseout
35645          * Fires when the mouse exits this menu
35646          * @param {Roo.menu.Menu} this
35647          * @param {Roo.EventObject} e
35648          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35649          */
35650         mouseout : true,
35651         /**
35652          * @event itemclick
35653          * Fires when a menu item contained in this menu is clicked
35654          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35655          * @param {Roo.EventObject} e
35656          */
35657         itemclick: true
35658     });
35659     if (this.registerMenu) {
35660         Roo.menu.MenuMgr.register(this);
35661     }
35662     
35663     var mis = this.items;
35664     this.items = new Roo.util.MixedCollection();
35665     if(mis){
35666         this.add.apply(this, mis);
35667     }
35668 };
35669
35670 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35671     /**
35672      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35673      */
35674     minWidth : 120,
35675     /**
35676      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35677      * for bottom-right shadow (defaults to "sides")
35678      */
35679     shadow : "sides",
35680     /**
35681      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35682      * this menu (defaults to "tl-tr?")
35683      */
35684     subMenuAlign : "tl-tr?",
35685     /**
35686      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35687      * relative to its element of origin (defaults to "tl-bl?")
35688      */
35689     defaultAlign : "tl-bl?",
35690     /**
35691      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35692      */
35693     allowOtherMenus : false,
35694     /**
35695      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35696      */
35697     registerMenu : true,
35698
35699     hidden:true,
35700
35701     // private
35702     render : function(){
35703         if(this.el){
35704             return;
35705         }
35706         var el = this.el = new Roo.Layer({
35707             cls: "x-menu",
35708             shadow:this.shadow,
35709             constrain: false,
35710             parentEl: this.parentEl || document.body,
35711             zindex:15000
35712         });
35713
35714         this.keyNav = new Roo.menu.MenuNav(this);
35715
35716         if(this.plain){
35717             el.addClass("x-menu-plain");
35718         }
35719         if(this.cls){
35720             el.addClass(this.cls);
35721         }
35722         // generic focus element
35723         this.focusEl = el.createChild({
35724             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35725         });
35726         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35727         //disabling touch- as it's causing issues ..
35728         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35729         ul.on('click'   , this.onClick, this);
35730         
35731         
35732         ul.on("mouseover", this.onMouseOver, this);
35733         ul.on("mouseout", this.onMouseOut, this);
35734         this.items.each(function(item){
35735             if (item.hidden) {
35736                 return;
35737             }
35738             
35739             var li = document.createElement("li");
35740             li.className = "x-menu-list-item";
35741             ul.dom.appendChild(li);
35742             item.render(li, this);
35743         }, this);
35744         this.ul = ul;
35745         this.autoWidth();
35746     },
35747
35748     // private
35749     autoWidth : function(){
35750         var el = this.el, ul = this.ul;
35751         if(!el){
35752             return;
35753         }
35754         var w = this.width;
35755         if(w){
35756             el.setWidth(w);
35757         }else if(Roo.isIE){
35758             el.setWidth(this.minWidth);
35759             var t = el.dom.offsetWidth; // force recalc
35760             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35761         }
35762     },
35763
35764     // private
35765     delayAutoWidth : function(){
35766         if(this.rendered){
35767             if(!this.awTask){
35768                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35769             }
35770             this.awTask.delay(20);
35771         }
35772     },
35773
35774     // private
35775     findTargetItem : function(e){
35776         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35777         if(t && t.menuItemId){
35778             return this.items.get(t.menuItemId);
35779         }
35780     },
35781
35782     // private
35783     onClick : function(e){
35784         Roo.log("menu.onClick");
35785         var t = this.findTargetItem(e);
35786         if(!t){
35787             return;
35788         }
35789         Roo.log(e);
35790         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35791             if(t == this.activeItem && t.shouldDeactivate(e)){
35792                 this.activeItem.deactivate();
35793                 delete this.activeItem;
35794                 return;
35795             }
35796             if(t.canActivate){
35797                 this.setActiveItem(t, true);
35798             }
35799             return;
35800             
35801             
35802         }
35803         
35804         t.onClick(e);
35805         this.fireEvent("click", this, t, e);
35806     },
35807
35808     // private
35809     setActiveItem : function(item, autoExpand){
35810         if(item != this.activeItem){
35811             if(this.activeItem){
35812                 this.activeItem.deactivate();
35813             }
35814             this.activeItem = item;
35815             item.activate(autoExpand);
35816         }else if(autoExpand){
35817             item.expandMenu();
35818         }
35819     },
35820
35821     // private
35822     tryActivate : function(start, step){
35823         var items = this.items;
35824         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35825             var item = items.get(i);
35826             if(!item.disabled && item.canActivate){
35827                 this.setActiveItem(item, false);
35828                 return item;
35829             }
35830         }
35831         return false;
35832     },
35833
35834     // private
35835     onMouseOver : function(e){
35836         var t;
35837         if(t = this.findTargetItem(e)){
35838             if(t.canActivate && !t.disabled){
35839                 this.setActiveItem(t, true);
35840             }
35841         }
35842         this.fireEvent("mouseover", this, e, t);
35843     },
35844
35845     // private
35846     onMouseOut : function(e){
35847         var t;
35848         if(t = this.findTargetItem(e)){
35849             if(t == this.activeItem && t.shouldDeactivate(e)){
35850                 this.activeItem.deactivate();
35851                 delete this.activeItem;
35852             }
35853         }
35854         this.fireEvent("mouseout", this, e, t);
35855     },
35856
35857     /**
35858      * Read-only.  Returns true if the menu is currently displayed, else false.
35859      * @type Boolean
35860      */
35861     isVisible : function(){
35862         return this.el && !this.hidden;
35863     },
35864
35865     /**
35866      * Displays this menu relative to another element
35867      * @param {String/HTMLElement/Roo.Element} element The element to align to
35868      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35869      * the element (defaults to this.defaultAlign)
35870      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35871      */
35872     show : function(el, pos, parentMenu){
35873         this.parentMenu = parentMenu;
35874         if(!this.el){
35875             this.render();
35876         }
35877         this.fireEvent("beforeshow", this);
35878         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35879     },
35880
35881     /**
35882      * Displays this menu at a specific xy position
35883      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35884      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35885      */
35886     showAt : function(xy, parentMenu, /* private: */_e){
35887         this.parentMenu = parentMenu;
35888         if(!this.el){
35889             this.render();
35890         }
35891         if(_e !== false){
35892             this.fireEvent("beforeshow", this);
35893             xy = this.el.adjustForConstraints(xy);
35894         }
35895         this.el.setXY(xy);
35896         this.el.show();
35897         this.hidden = false;
35898         this.focus();
35899         this.fireEvent("show", this);
35900     },
35901
35902     focus : function(){
35903         if(!this.hidden){
35904             this.doFocus.defer(50, this);
35905         }
35906     },
35907
35908     doFocus : function(){
35909         if(!this.hidden){
35910             this.focusEl.focus();
35911         }
35912     },
35913
35914     /**
35915      * Hides this menu and optionally all parent menus
35916      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35917      */
35918     hide : function(deep){
35919         if(this.el && this.isVisible()){
35920             this.fireEvent("beforehide", this);
35921             if(this.activeItem){
35922                 this.activeItem.deactivate();
35923                 this.activeItem = null;
35924             }
35925             this.el.hide();
35926             this.hidden = true;
35927             this.fireEvent("hide", this);
35928         }
35929         if(deep === true && this.parentMenu){
35930             this.parentMenu.hide(true);
35931         }
35932     },
35933
35934     /**
35935      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35936      * Any of the following are valid:
35937      * <ul>
35938      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35939      * <li>An HTMLElement object which will be converted to a menu item</li>
35940      * <li>A menu item config object that will be created as a new menu item</li>
35941      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35942      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35943      * </ul>
35944      * Usage:
35945      * <pre><code>
35946 // Create the menu
35947 var menu = new Roo.menu.Menu();
35948
35949 // Create a menu item to add by reference
35950 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35951
35952 // Add a bunch of items at once using different methods.
35953 // Only the last item added will be returned.
35954 var item = menu.add(
35955     menuItem,                // add existing item by ref
35956     'Dynamic Item',          // new TextItem
35957     '-',                     // new separator
35958     { text: 'Config Item' }  // new item by config
35959 );
35960 </code></pre>
35961      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35962      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35963      */
35964     add : function(){
35965         var a = arguments, l = a.length, item;
35966         for(var i = 0; i < l; i++){
35967             var el = a[i];
35968             if ((typeof(el) == "object") && el.xtype && el.xns) {
35969                 el = Roo.factory(el, Roo.menu);
35970             }
35971             
35972             if(el.render){ // some kind of Item
35973                 item = this.addItem(el);
35974             }else if(typeof el == "string"){ // string
35975                 if(el == "separator" || el == "-"){
35976                     item = this.addSeparator();
35977                 }else{
35978                     item = this.addText(el);
35979                 }
35980             }else if(el.tagName || el.el){ // element
35981                 item = this.addElement(el);
35982             }else if(typeof el == "object"){ // must be menu item config?
35983                 item = this.addMenuItem(el);
35984             }
35985         }
35986         return item;
35987     },
35988
35989     /**
35990      * Returns this menu's underlying {@link Roo.Element} object
35991      * @return {Roo.Element} The element
35992      */
35993     getEl : function(){
35994         if(!this.el){
35995             this.render();
35996         }
35997         return this.el;
35998     },
35999
36000     /**
36001      * Adds a separator bar to the menu
36002      * @return {Roo.menu.Item} The menu item that was added
36003      */
36004     addSeparator : function(){
36005         return this.addItem(new Roo.menu.Separator());
36006     },
36007
36008     /**
36009      * Adds an {@link Roo.Element} object to the menu
36010      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
36011      * @return {Roo.menu.Item} The menu item that was added
36012      */
36013     addElement : function(el){
36014         return this.addItem(new Roo.menu.BaseItem(el));
36015     },
36016
36017     /**
36018      * Adds an existing object based on {@link Roo.menu.Item} to the menu
36019      * @param {Roo.menu.Item} item The menu item to add
36020      * @return {Roo.menu.Item} The menu item that was added
36021      */
36022     addItem : function(item){
36023         this.items.add(item);
36024         if(this.ul){
36025             var li = document.createElement("li");
36026             li.className = "x-menu-list-item";
36027             this.ul.dom.appendChild(li);
36028             item.render(li, this);
36029             this.delayAutoWidth();
36030         }
36031         return item;
36032     },
36033
36034     /**
36035      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
36036      * @param {Object} config A MenuItem config object
36037      * @return {Roo.menu.Item} The menu item that was added
36038      */
36039     addMenuItem : function(config){
36040         if(!(config instanceof Roo.menu.Item)){
36041             if(typeof config.checked == "boolean"){ // must be check menu item config?
36042                 config = new Roo.menu.CheckItem(config);
36043             }else{
36044                 config = new Roo.menu.Item(config);
36045             }
36046         }
36047         return this.addItem(config);
36048     },
36049
36050     /**
36051      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36052      * @param {String} text The text to display in the menu item
36053      * @return {Roo.menu.Item} The menu item that was added
36054      */
36055     addText : function(text){
36056         return this.addItem(new Roo.menu.TextItem({ text : text }));
36057     },
36058
36059     /**
36060      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36061      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36062      * @param {Roo.menu.Item} item The menu item to add
36063      * @return {Roo.menu.Item} The menu item that was added
36064      */
36065     insert : function(index, item){
36066         this.items.insert(index, item);
36067         if(this.ul){
36068             var li = document.createElement("li");
36069             li.className = "x-menu-list-item";
36070             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36071             item.render(li, this);
36072             this.delayAutoWidth();
36073         }
36074         return item;
36075     },
36076
36077     /**
36078      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36079      * @param {Roo.menu.Item} item The menu item to remove
36080      */
36081     remove : function(item){
36082         this.items.removeKey(item.id);
36083         item.destroy();
36084     },
36085
36086     /**
36087      * Removes and destroys all items in the menu
36088      */
36089     removeAll : function(){
36090         var f;
36091         while(f = this.items.first()){
36092             this.remove(f);
36093         }
36094     }
36095 });
36096
36097 // MenuNav is a private utility class used internally by the Menu
36098 Roo.menu.MenuNav = function(menu){
36099     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36100     this.scope = this.menu = menu;
36101 };
36102
36103 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36104     doRelay : function(e, h){
36105         var k = e.getKey();
36106         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36107             this.menu.tryActivate(0, 1);
36108             return false;
36109         }
36110         return h.call(this.scope || this, e, this.menu);
36111     },
36112
36113     up : function(e, m){
36114         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36115             m.tryActivate(m.items.length-1, -1);
36116         }
36117     },
36118
36119     down : function(e, m){
36120         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36121             m.tryActivate(0, 1);
36122         }
36123     },
36124
36125     right : function(e, m){
36126         if(m.activeItem){
36127             m.activeItem.expandMenu(true);
36128         }
36129     },
36130
36131     left : function(e, m){
36132         m.hide();
36133         if(m.parentMenu && m.parentMenu.activeItem){
36134             m.parentMenu.activeItem.activate();
36135         }
36136     },
36137
36138     enter : function(e, m){
36139         if(m.activeItem){
36140             e.stopPropagation();
36141             m.activeItem.onClick(e);
36142             m.fireEvent("click", this, m.activeItem);
36143             return true;
36144         }
36145     }
36146 });/*
36147  * Based on:
36148  * Ext JS Library 1.1.1
36149  * Copyright(c) 2006-2007, Ext JS, LLC.
36150  *
36151  * Originally Released Under LGPL - original licence link has changed is not relivant.
36152  *
36153  * Fork - LGPL
36154  * <script type="text/javascript">
36155  */
36156  
36157 /**
36158  * @class Roo.menu.MenuMgr
36159  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36160  * @singleton
36161  */
36162 Roo.menu.MenuMgr = function(){
36163    var menus, active, groups = {}, attached = false, lastShow = new Date();
36164
36165    // private - called when first menu is created
36166    function init(){
36167        menus = {};
36168        active = new Roo.util.MixedCollection();
36169        Roo.get(document).addKeyListener(27, function(){
36170            if(active.length > 0){
36171                hideAll();
36172            }
36173        });
36174    }
36175
36176    // private
36177    function hideAll(){
36178        if(active && active.length > 0){
36179            var c = active.clone();
36180            c.each(function(m){
36181                m.hide();
36182            });
36183        }
36184    }
36185
36186    // private
36187    function onHide(m){
36188        active.remove(m);
36189        if(active.length < 1){
36190            Roo.get(document).un("mousedown", onMouseDown);
36191            attached = false;
36192        }
36193    }
36194
36195    // private
36196    function onShow(m){
36197        var last = active.last();
36198        lastShow = new Date();
36199        active.add(m);
36200        if(!attached){
36201            Roo.get(document).on("mousedown", onMouseDown);
36202            attached = true;
36203        }
36204        if(m.parentMenu){
36205           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36206           m.parentMenu.activeChild = m;
36207        }else if(last && last.isVisible()){
36208           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36209        }
36210    }
36211
36212    // private
36213    function onBeforeHide(m){
36214        if(m.activeChild){
36215            m.activeChild.hide();
36216        }
36217        if(m.autoHideTimer){
36218            clearTimeout(m.autoHideTimer);
36219            delete m.autoHideTimer;
36220        }
36221    }
36222
36223    // private
36224    function onBeforeShow(m){
36225        var pm = m.parentMenu;
36226        if(!pm && !m.allowOtherMenus){
36227            hideAll();
36228        }else if(pm && pm.activeChild && active != m){
36229            pm.activeChild.hide();
36230        }
36231    }
36232
36233    // private
36234    function onMouseDown(e){
36235        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36236            hideAll();
36237        }
36238    }
36239
36240    // private
36241    function onBeforeCheck(mi, state){
36242        if(state){
36243            var g = groups[mi.group];
36244            for(var i = 0, l = g.length; i < l; i++){
36245                if(g[i] != mi){
36246                    g[i].setChecked(false);
36247                }
36248            }
36249        }
36250    }
36251
36252    return {
36253
36254        /**
36255         * Hides all menus that are currently visible
36256         */
36257        hideAll : function(){
36258             hideAll();  
36259        },
36260
36261        // private
36262        register : function(menu){
36263            if(!menus){
36264                init();
36265            }
36266            menus[menu.id] = menu;
36267            menu.on("beforehide", onBeforeHide);
36268            menu.on("hide", onHide);
36269            menu.on("beforeshow", onBeforeShow);
36270            menu.on("show", onShow);
36271            var g = menu.group;
36272            if(g && menu.events["checkchange"]){
36273                if(!groups[g]){
36274                    groups[g] = [];
36275                }
36276                groups[g].push(menu);
36277                menu.on("checkchange", onCheck);
36278            }
36279        },
36280
36281         /**
36282          * Returns a {@link Roo.menu.Menu} object
36283          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36284          * be used to generate and return a new Menu instance.
36285          */
36286        get : function(menu){
36287            if(typeof menu == "string"){ // menu id
36288                return menus[menu];
36289            }else if(menu.events){  // menu instance
36290                return menu;
36291            }else if(typeof menu.length == 'number'){ // array of menu items?
36292                return new Roo.menu.Menu({items:menu});
36293            }else{ // otherwise, must be a config
36294                return new Roo.menu.Menu(menu);
36295            }
36296        },
36297
36298        // private
36299        unregister : function(menu){
36300            delete menus[menu.id];
36301            menu.un("beforehide", onBeforeHide);
36302            menu.un("hide", onHide);
36303            menu.un("beforeshow", onBeforeShow);
36304            menu.un("show", onShow);
36305            var g = menu.group;
36306            if(g && menu.events["checkchange"]){
36307                groups[g].remove(menu);
36308                menu.un("checkchange", onCheck);
36309            }
36310        },
36311
36312        // private
36313        registerCheckable : function(menuItem){
36314            var g = menuItem.group;
36315            if(g){
36316                if(!groups[g]){
36317                    groups[g] = [];
36318                }
36319                groups[g].push(menuItem);
36320                menuItem.on("beforecheckchange", onBeforeCheck);
36321            }
36322        },
36323
36324        // private
36325        unregisterCheckable : function(menuItem){
36326            var g = menuItem.group;
36327            if(g){
36328                groups[g].remove(menuItem);
36329                menuItem.un("beforecheckchange", onBeforeCheck);
36330            }
36331        }
36332    };
36333 }();/*
36334  * Based on:
36335  * Ext JS Library 1.1.1
36336  * Copyright(c) 2006-2007, Ext JS, LLC.
36337  *
36338  * Originally Released Under LGPL - original licence link has changed is not relivant.
36339  *
36340  * Fork - LGPL
36341  * <script type="text/javascript">
36342  */
36343  
36344
36345 /**
36346  * @class Roo.menu.BaseItem
36347  * @extends Roo.Component
36348  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36349  * management and base configuration options shared by all menu components.
36350  * @constructor
36351  * Creates a new BaseItem
36352  * @param {Object} config Configuration options
36353  */
36354 Roo.menu.BaseItem = function(config){
36355     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36356
36357     this.addEvents({
36358         /**
36359          * @event click
36360          * Fires when this item is clicked
36361          * @param {Roo.menu.BaseItem} this
36362          * @param {Roo.EventObject} e
36363          */
36364         click: true,
36365         /**
36366          * @event activate
36367          * Fires when this item is activated
36368          * @param {Roo.menu.BaseItem} this
36369          */
36370         activate : true,
36371         /**
36372          * @event deactivate
36373          * Fires when this item is deactivated
36374          * @param {Roo.menu.BaseItem} this
36375          */
36376         deactivate : true
36377     });
36378
36379     if(this.handler){
36380         this.on("click", this.handler, this.scope, true);
36381     }
36382 };
36383
36384 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36385     /**
36386      * @cfg {Function} handler
36387      * A function that will handle the click event of this menu item (defaults to undefined)
36388      */
36389     /**
36390      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36391      */
36392     canActivate : false,
36393     
36394      /**
36395      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36396      */
36397     hidden: false,
36398     
36399     /**
36400      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36401      */
36402     activeClass : "x-menu-item-active",
36403     /**
36404      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36405      */
36406     hideOnClick : true,
36407     /**
36408      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36409      */
36410     hideDelay : 100,
36411
36412     // private
36413     ctype: "Roo.menu.BaseItem",
36414
36415     // private
36416     actionMode : "container",
36417
36418     // private
36419     render : function(container, parentMenu){
36420         this.parentMenu = parentMenu;
36421         Roo.menu.BaseItem.superclass.render.call(this, container);
36422         this.container.menuItemId = this.id;
36423     },
36424
36425     // private
36426     onRender : function(container, position){
36427         this.el = Roo.get(this.el);
36428         container.dom.appendChild(this.el.dom);
36429     },
36430
36431     // private
36432     onClick : function(e){
36433         if(!this.disabled && this.fireEvent("click", this, e) !== false
36434                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36435             this.handleClick(e);
36436         }else{
36437             e.stopEvent();
36438         }
36439     },
36440
36441     // private
36442     activate : function(){
36443         if(this.disabled){
36444             return false;
36445         }
36446         var li = this.container;
36447         li.addClass(this.activeClass);
36448         this.region = li.getRegion().adjust(2, 2, -2, -2);
36449         this.fireEvent("activate", this);
36450         return true;
36451     },
36452
36453     // private
36454     deactivate : function(){
36455         this.container.removeClass(this.activeClass);
36456         this.fireEvent("deactivate", this);
36457     },
36458
36459     // private
36460     shouldDeactivate : function(e){
36461         return !this.region || !this.region.contains(e.getPoint());
36462     },
36463
36464     // private
36465     handleClick : function(e){
36466         if(this.hideOnClick){
36467             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36468         }
36469     },
36470
36471     // private
36472     expandMenu : function(autoActivate){
36473         // do nothing
36474     },
36475
36476     // private
36477     hideMenu : function(){
36478         // do nothing
36479     }
36480 });/*
36481  * Based on:
36482  * Ext JS Library 1.1.1
36483  * Copyright(c) 2006-2007, Ext JS, LLC.
36484  *
36485  * Originally Released Under LGPL - original licence link has changed is not relivant.
36486  *
36487  * Fork - LGPL
36488  * <script type="text/javascript">
36489  */
36490  
36491 /**
36492  * @class Roo.menu.Adapter
36493  * @extends Roo.menu.BaseItem
36494  * 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.
36495  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36496  * @constructor
36497  * Creates a new Adapter
36498  * @param {Object} config Configuration options
36499  */
36500 Roo.menu.Adapter = function(component, config){
36501     Roo.menu.Adapter.superclass.constructor.call(this, config);
36502     this.component = component;
36503 };
36504 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36505     // private
36506     canActivate : true,
36507
36508     // private
36509     onRender : function(container, position){
36510         this.component.render(container);
36511         this.el = this.component.getEl();
36512     },
36513
36514     // private
36515     activate : function(){
36516         if(this.disabled){
36517             return false;
36518         }
36519         this.component.focus();
36520         this.fireEvent("activate", this);
36521         return true;
36522     },
36523
36524     // private
36525     deactivate : function(){
36526         this.fireEvent("deactivate", this);
36527     },
36528
36529     // private
36530     disable : function(){
36531         this.component.disable();
36532         Roo.menu.Adapter.superclass.disable.call(this);
36533     },
36534
36535     // private
36536     enable : function(){
36537         this.component.enable();
36538         Roo.menu.Adapter.superclass.enable.call(this);
36539     }
36540 });/*
36541  * Based on:
36542  * Ext JS Library 1.1.1
36543  * Copyright(c) 2006-2007, Ext JS, LLC.
36544  *
36545  * Originally Released Under LGPL - original licence link has changed is not relivant.
36546  *
36547  * Fork - LGPL
36548  * <script type="text/javascript">
36549  */
36550
36551 /**
36552  * @class Roo.menu.TextItem
36553  * @extends Roo.menu.BaseItem
36554  * Adds a static text string to a menu, usually used as either a heading or group separator.
36555  * Note: old style constructor with text is still supported.
36556  * 
36557  * @constructor
36558  * Creates a new TextItem
36559  * @param {Object} cfg Configuration
36560  */
36561 Roo.menu.TextItem = function(cfg){
36562     if (typeof(cfg) == 'string') {
36563         this.text = cfg;
36564     } else {
36565         Roo.apply(this,cfg);
36566     }
36567     
36568     Roo.menu.TextItem.superclass.constructor.call(this);
36569 };
36570
36571 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36572     /**
36573      * @cfg {Boolean} text Text to show on item.
36574      */
36575     text : '',
36576     
36577     /**
36578      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36579      */
36580     hideOnClick : false,
36581     /**
36582      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36583      */
36584     itemCls : "x-menu-text",
36585
36586     // private
36587     onRender : function(){
36588         var s = document.createElement("span");
36589         s.className = this.itemCls;
36590         s.innerHTML = this.text;
36591         this.el = s;
36592         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36593     }
36594 });/*
36595  * Based on:
36596  * Ext JS Library 1.1.1
36597  * Copyright(c) 2006-2007, Ext JS, LLC.
36598  *
36599  * Originally Released Under LGPL - original licence link has changed is not relivant.
36600  *
36601  * Fork - LGPL
36602  * <script type="text/javascript">
36603  */
36604
36605 /**
36606  * @class Roo.menu.Separator
36607  * @extends Roo.menu.BaseItem
36608  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36609  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36610  * @constructor
36611  * @param {Object} config Configuration options
36612  */
36613 Roo.menu.Separator = function(config){
36614     Roo.menu.Separator.superclass.constructor.call(this, config);
36615 };
36616
36617 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36618     /**
36619      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36620      */
36621     itemCls : "x-menu-sep",
36622     /**
36623      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36624      */
36625     hideOnClick : false,
36626
36627     // private
36628     onRender : function(li){
36629         var s = document.createElement("span");
36630         s.className = this.itemCls;
36631         s.innerHTML = "&#160;";
36632         this.el = s;
36633         li.addClass("x-menu-sep-li");
36634         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36635     }
36636 });/*
36637  * Based on:
36638  * Ext JS Library 1.1.1
36639  * Copyright(c) 2006-2007, Ext JS, LLC.
36640  *
36641  * Originally Released Under LGPL - original licence link has changed is not relivant.
36642  *
36643  * Fork - LGPL
36644  * <script type="text/javascript">
36645  */
36646 /**
36647  * @class Roo.menu.Item
36648  * @extends Roo.menu.BaseItem
36649  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36650  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36651  * activation and click handling.
36652  * @constructor
36653  * Creates a new Item
36654  * @param {Object} config Configuration options
36655  */
36656 Roo.menu.Item = function(config){
36657     Roo.menu.Item.superclass.constructor.call(this, config);
36658     if(this.menu){
36659         this.menu = Roo.menu.MenuMgr.get(this.menu);
36660     }
36661 };
36662 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36663     
36664     /**
36665      * @cfg {String} text
36666      * The text to show on the menu item.
36667      */
36668     text: '',
36669      /**
36670      * @cfg {String} HTML to render in menu
36671      * The text to show on the menu item (HTML version).
36672      */
36673     html: '',
36674     /**
36675      * @cfg {String} icon
36676      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36677      */
36678     icon: undefined,
36679     /**
36680      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36681      */
36682     itemCls : "x-menu-item",
36683     /**
36684      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36685      */
36686     canActivate : true,
36687     /**
36688      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36689      */
36690     showDelay: 200,
36691     // doc'd in BaseItem
36692     hideDelay: 200,
36693
36694     // private
36695     ctype: "Roo.menu.Item",
36696     
36697     // private
36698     onRender : function(container, position){
36699         var el = document.createElement("a");
36700         el.hideFocus = true;
36701         el.unselectable = "on";
36702         el.href = this.href || "#";
36703         if(this.hrefTarget){
36704             el.target = this.hrefTarget;
36705         }
36706         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36707         
36708         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36709         
36710         el.innerHTML = String.format(
36711                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36712                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36713         this.el = el;
36714         Roo.menu.Item.superclass.onRender.call(this, container, position);
36715     },
36716
36717     /**
36718      * Sets the text to display in this menu item
36719      * @param {String} text The text to display
36720      * @param {Boolean} isHTML true to indicate text is pure html.
36721      */
36722     setText : function(text, isHTML){
36723         if (isHTML) {
36724             this.html = text;
36725         } else {
36726             this.text = text;
36727             this.html = '';
36728         }
36729         if(this.rendered){
36730             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36731      
36732             this.el.update(String.format(
36733                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36734                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36735             this.parentMenu.autoWidth();
36736         }
36737     },
36738
36739     // private
36740     handleClick : function(e){
36741         if(!this.href){ // if no link defined, stop the event automatically
36742             e.stopEvent();
36743         }
36744         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36745     },
36746
36747     // private
36748     activate : function(autoExpand){
36749         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36750             this.focus();
36751             if(autoExpand){
36752                 this.expandMenu();
36753             }
36754         }
36755         return true;
36756     },
36757
36758     // private
36759     shouldDeactivate : function(e){
36760         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36761             if(this.menu && this.menu.isVisible()){
36762                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36763             }
36764             return true;
36765         }
36766         return false;
36767     },
36768
36769     // private
36770     deactivate : function(){
36771         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36772         this.hideMenu();
36773     },
36774
36775     // private
36776     expandMenu : function(autoActivate){
36777         if(!this.disabled && this.menu){
36778             clearTimeout(this.hideTimer);
36779             delete this.hideTimer;
36780             if(!this.menu.isVisible() && !this.showTimer){
36781                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36782             }else if (this.menu.isVisible() && autoActivate){
36783                 this.menu.tryActivate(0, 1);
36784             }
36785         }
36786     },
36787
36788     // private
36789     deferExpand : function(autoActivate){
36790         delete this.showTimer;
36791         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36792         if(autoActivate){
36793             this.menu.tryActivate(0, 1);
36794         }
36795     },
36796
36797     // private
36798     hideMenu : function(){
36799         clearTimeout(this.showTimer);
36800         delete this.showTimer;
36801         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36802             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36803         }
36804     },
36805
36806     // private
36807     deferHide : function(){
36808         delete this.hideTimer;
36809         this.menu.hide();
36810     }
36811 });/*
36812  * Based on:
36813  * Ext JS Library 1.1.1
36814  * Copyright(c) 2006-2007, Ext JS, LLC.
36815  *
36816  * Originally Released Under LGPL - original licence link has changed is not relivant.
36817  *
36818  * Fork - LGPL
36819  * <script type="text/javascript">
36820  */
36821  
36822 /**
36823  * @class Roo.menu.CheckItem
36824  * @extends Roo.menu.Item
36825  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36826  * @constructor
36827  * Creates a new CheckItem
36828  * @param {Object} config Configuration options
36829  */
36830 Roo.menu.CheckItem = function(config){
36831     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36832     this.addEvents({
36833         /**
36834          * @event beforecheckchange
36835          * Fires before the checked value is set, providing an opportunity to cancel if needed
36836          * @param {Roo.menu.CheckItem} this
36837          * @param {Boolean} checked The new checked value that will be set
36838          */
36839         "beforecheckchange" : true,
36840         /**
36841          * @event checkchange
36842          * Fires after the checked value has been set
36843          * @param {Roo.menu.CheckItem} this
36844          * @param {Boolean} checked The checked value that was set
36845          */
36846         "checkchange" : true
36847     });
36848     if(this.checkHandler){
36849         this.on('checkchange', this.checkHandler, this.scope);
36850     }
36851 };
36852 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36853     /**
36854      * @cfg {String} group
36855      * All check items with the same group name will automatically be grouped into a single-select
36856      * radio button group (defaults to '')
36857      */
36858     /**
36859      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36860      */
36861     itemCls : "x-menu-item x-menu-check-item",
36862     /**
36863      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36864      */
36865     groupClass : "x-menu-group-item",
36866
36867     /**
36868      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36869      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36870      * initialized with checked = true will be rendered as checked.
36871      */
36872     checked: false,
36873
36874     // private
36875     ctype: "Roo.menu.CheckItem",
36876
36877     // private
36878     onRender : function(c){
36879         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36880         if(this.group){
36881             this.el.addClass(this.groupClass);
36882         }
36883         Roo.menu.MenuMgr.registerCheckable(this);
36884         if(this.checked){
36885             this.checked = false;
36886             this.setChecked(true, true);
36887         }
36888     },
36889
36890     // private
36891     destroy : function(){
36892         if(this.rendered){
36893             Roo.menu.MenuMgr.unregisterCheckable(this);
36894         }
36895         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36896     },
36897
36898     /**
36899      * Set the checked state of this item
36900      * @param {Boolean} checked The new checked value
36901      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36902      */
36903     setChecked : function(state, suppressEvent){
36904         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36905             if(this.container){
36906                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36907             }
36908             this.checked = state;
36909             if(suppressEvent !== true){
36910                 this.fireEvent("checkchange", this, state);
36911             }
36912         }
36913     },
36914
36915     // private
36916     handleClick : function(e){
36917        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36918            this.setChecked(!this.checked);
36919        }
36920        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36921     }
36922 });/*
36923  * Based on:
36924  * Ext JS Library 1.1.1
36925  * Copyright(c) 2006-2007, Ext JS, LLC.
36926  *
36927  * Originally Released Under LGPL - original licence link has changed is not relivant.
36928  *
36929  * Fork - LGPL
36930  * <script type="text/javascript">
36931  */
36932  
36933 /**
36934  * @class Roo.menu.DateItem
36935  * @extends Roo.menu.Adapter
36936  * A menu item that wraps the {@link Roo.DatPicker} component.
36937  * @constructor
36938  * Creates a new DateItem
36939  * @param {Object} config Configuration options
36940  */
36941 Roo.menu.DateItem = function(config){
36942     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36943     /** The Roo.DatePicker object @type Roo.DatePicker */
36944     this.picker = this.component;
36945     this.addEvents({select: true});
36946     
36947     this.picker.on("render", function(picker){
36948         picker.getEl().swallowEvent("click");
36949         picker.container.addClass("x-menu-date-item");
36950     });
36951
36952     this.picker.on("select", this.onSelect, this);
36953 };
36954
36955 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36956     // private
36957     onSelect : function(picker, date){
36958         this.fireEvent("select", this, date, picker);
36959         Roo.menu.DateItem.superclass.handleClick.call(this);
36960     }
36961 });/*
36962  * Based on:
36963  * Ext JS Library 1.1.1
36964  * Copyright(c) 2006-2007, Ext JS, LLC.
36965  *
36966  * Originally Released Under LGPL - original licence link has changed is not relivant.
36967  *
36968  * Fork - LGPL
36969  * <script type="text/javascript">
36970  */
36971  
36972 /**
36973  * @class Roo.menu.ColorItem
36974  * @extends Roo.menu.Adapter
36975  * A menu item that wraps the {@link Roo.ColorPalette} component.
36976  * @constructor
36977  * Creates a new ColorItem
36978  * @param {Object} config Configuration options
36979  */
36980 Roo.menu.ColorItem = function(config){
36981     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36982     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36983     this.palette = this.component;
36984     this.relayEvents(this.palette, ["select"]);
36985     if(this.selectHandler){
36986         this.on('select', this.selectHandler, this.scope);
36987     }
36988 };
36989 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36990  * Based on:
36991  * Ext JS Library 1.1.1
36992  * Copyright(c) 2006-2007, Ext JS, LLC.
36993  *
36994  * Originally Released Under LGPL - original licence link has changed is not relivant.
36995  *
36996  * Fork - LGPL
36997  * <script type="text/javascript">
36998  */
36999  
37000
37001 /**
37002  * @class Roo.menu.DateMenu
37003  * @extends Roo.menu.Menu
37004  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
37005  * @constructor
37006  * Creates a new DateMenu
37007  * @param {Object} config Configuration options
37008  */
37009 Roo.menu.DateMenu = function(config){
37010     Roo.menu.DateMenu.superclass.constructor.call(this, config);
37011     this.plain = true;
37012     var di = new Roo.menu.DateItem(config);
37013     this.add(di);
37014     /**
37015      * The {@link Roo.DatePicker} instance for this DateMenu
37016      * @type DatePicker
37017      */
37018     this.picker = di.picker;
37019     /**
37020      * @event select
37021      * @param {DatePicker} picker
37022      * @param {Date} date
37023      */
37024     this.relayEvents(di, ["select"]);
37025     this.on('beforeshow', function(){
37026         if(this.picker){
37027             this.picker.hideMonthPicker(false);
37028         }
37029     }, this);
37030 };
37031 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
37032     cls:'x-date-menu'
37033 });/*
37034  * Based on:
37035  * Ext JS Library 1.1.1
37036  * Copyright(c) 2006-2007, Ext JS, LLC.
37037  *
37038  * Originally Released Under LGPL - original licence link has changed is not relivant.
37039  *
37040  * Fork - LGPL
37041  * <script type="text/javascript">
37042  */
37043  
37044
37045 /**
37046  * @class Roo.menu.ColorMenu
37047  * @extends Roo.menu.Menu
37048  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37049  * @constructor
37050  * Creates a new ColorMenu
37051  * @param {Object} config Configuration options
37052  */
37053 Roo.menu.ColorMenu = function(config){
37054     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37055     this.plain = true;
37056     var ci = new Roo.menu.ColorItem(config);
37057     this.add(ci);
37058     /**
37059      * The {@link Roo.ColorPalette} instance for this ColorMenu
37060      * @type ColorPalette
37061      */
37062     this.palette = ci.palette;
37063     /**
37064      * @event select
37065      * @param {ColorPalette} palette
37066      * @param {String} color
37067      */
37068     this.relayEvents(ci, ["select"]);
37069 };
37070 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37071  * Based on:
37072  * Ext JS Library 1.1.1
37073  * Copyright(c) 2006-2007, Ext JS, LLC.
37074  *
37075  * Originally Released Under LGPL - original licence link has changed is not relivant.
37076  *
37077  * Fork - LGPL
37078  * <script type="text/javascript">
37079  */
37080  
37081 /**
37082  * @class Roo.form.Field
37083  * @extends Roo.BoxComponent
37084  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37085  * @constructor
37086  * Creates a new Field
37087  * @param {Object} config Configuration options
37088  */
37089 Roo.form.Field = function(config){
37090     Roo.form.Field.superclass.constructor.call(this, config);
37091 };
37092
37093 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37094     /**
37095      * @cfg {String} fieldLabel Label to use when rendering a form.
37096      */
37097        /**
37098      * @cfg {String} qtip Mouse over tip
37099      */
37100      
37101     /**
37102      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37103      */
37104     invalidClass : "x-form-invalid",
37105     /**
37106      * @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")
37107      */
37108     invalidText : "The value in this field is invalid",
37109     /**
37110      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37111      */
37112     focusClass : "x-form-focus",
37113     /**
37114      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37115       automatic validation (defaults to "keyup").
37116      */
37117     validationEvent : "keyup",
37118     /**
37119      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37120      */
37121     validateOnBlur : true,
37122     /**
37123      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37124      */
37125     validationDelay : 250,
37126     /**
37127      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37128      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37129      */
37130     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37131     /**
37132      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37133      */
37134     fieldClass : "x-form-field",
37135     /**
37136      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37137      *<pre>
37138 Value         Description
37139 -----------   ----------------------------------------------------------------------
37140 qtip          Display a quick tip when the user hovers over the field
37141 title         Display a default browser title attribute popup
37142 under         Add a block div beneath the field containing the error text
37143 side          Add an error icon to the right of the field with a popup on hover
37144 [element id]  Add the error text directly to the innerHTML of the specified element
37145 </pre>
37146      */
37147     msgTarget : 'qtip',
37148     /**
37149      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37150      */
37151     msgFx : 'normal',
37152
37153     /**
37154      * @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.
37155      */
37156     readOnly : false,
37157
37158     /**
37159      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37160      */
37161     disabled : false,
37162
37163     /**
37164      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37165      */
37166     inputType : undefined,
37167     
37168     /**
37169      * @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).
37170          */
37171         tabIndex : undefined,
37172         
37173     // private
37174     isFormField : true,
37175
37176     // private
37177     hasFocus : false,
37178     /**
37179      * @property {Roo.Element} fieldEl
37180      * Element Containing the rendered Field (with label etc.)
37181      */
37182     /**
37183      * @cfg {Mixed} value A value to initialize this field with.
37184      */
37185     value : undefined,
37186
37187     /**
37188      * @cfg {String} name The field's HTML name attribute.
37189      */
37190     /**
37191      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37192      */
37193
37194         // private ??
37195         initComponent : function(){
37196         Roo.form.Field.superclass.initComponent.call(this);
37197         this.addEvents({
37198             /**
37199              * @event focus
37200              * Fires when this field receives input focus.
37201              * @param {Roo.form.Field} this
37202              */
37203             focus : true,
37204             /**
37205              * @event blur
37206              * Fires when this field loses input focus.
37207              * @param {Roo.form.Field} this
37208              */
37209             blur : true,
37210             /**
37211              * @event specialkey
37212              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37213              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37214              * @param {Roo.form.Field} this
37215              * @param {Roo.EventObject} e The event object
37216              */
37217             specialkey : true,
37218             /**
37219              * @event change
37220              * Fires just before the field blurs if the field value has changed.
37221              * @param {Roo.form.Field} this
37222              * @param {Mixed} newValue The new value
37223              * @param {Mixed} oldValue The original value
37224              */
37225             change : true,
37226             /**
37227              * @event invalid
37228              * Fires after the field has been marked as invalid.
37229              * @param {Roo.form.Field} this
37230              * @param {String} msg The validation message
37231              */
37232             invalid : true,
37233             /**
37234              * @event valid
37235              * Fires after the field has been validated with no errors.
37236              * @param {Roo.form.Field} this
37237              */
37238             valid : true,
37239              /**
37240              * @event keyup
37241              * Fires after the key up
37242              * @param {Roo.form.Field} this
37243              * @param {Roo.EventObject}  e The event Object
37244              */
37245             keyup : true
37246         });
37247     },
37248
37249     /**
37250      * Returns the name attribute of the field if available
37251      * @return {String} name The field name
37252      */
37253     getName: function(){
37254          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37255     },
37256
37257     // private
37258     onRender : function(ct, position){
37259         Roo.form.Field.superclass.onRender.call(this, ct, position);
37260         if(!this.el){
37261             var cfg = this.getAutoCreate();
37262             if(!cfg.name){
37263                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37264             }
37265             if (!cfg.name.length) {
37266                 delete cfg.name;
37267             }
37268             if(this.inputType){
37269                 cfg.type = this.inputType;
37270             }
37271             this.el = ct.createChild(cfg, position);
37272         }
37273         var type = this.el.dom.type;
37274         if(type){
37275             if(type == 'password'){
37276                 type = 'text';
37277             }
37278             this.el.addClass('x-form-'+type);
37279         }
37280         if(this.readOnly){
37281             this.el.dom.readOnly = true;
37282         }
37283         if(this.tabIndex !== undefined){
37284             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37285         }
37286
37287         this.el.addClass([this.fieldClass, this.cls]);
37288         this.initValue();
37289     },
37290
37291     /**
37292      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37293      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37294      * @return {Roo.form.Field} this
37295      */
37296     applyTo : function(target){
37297         this.allowDomMove = false;
37298         this.el = Roo.get(target);
37299         this.render(this.el.dom.parentNode);
37300         return this;
37301     },
37302
37303     // private
37304     initValue : function(){
37305         if(this.value !== undefined){
37306             this.setValue(this.value);
37307         }else if(this.el.dom.value.length > 0){
37308             this.setValue(this.el.dom.value);
37309         }
37310     },
37311
37312     /**
37313      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37314      */
37315     isDirty : function() {
37316         if(this.disabled) {
37317             return false;
37318         }
37319         return String(this.getValue()) !== String(this.originalValue);
37320     },
37321
37322     // private
37323     afterRender : function(){
37324         Roo.form.Field.superclass.afterRender.call(this);
37325         this.initEvents();
37326     },
37327
37328     // private
37329     fireKey : function(e){
37330         //Roo.log('field ' + e.getKey());
37331         if(e.isNavKeyPress()){
37332             this.fireEvent("specialkey", this, e);
37333         }
37334     },
37335
37336     /**
37337      * Resets the current field value to the originally loaded value and clears any validation messages
37338      */
37339     reset : function(){
37340         this.setValue(this.resetValue);
37341         this.clearInvalid();
37342     },
37343
37344     // private
37345     initEvents : function(){
37346         // safari killled keypress - so keydown is now used..
37347         this.el.on("keydown" , this.fireKey,  this);
37348         this.el.on("focus", this.onFocus,  this);
37349         this.el.on("blur", this.onBlur,  this);
37350         this.el.relayEvent('keyup', this);
37351
37352         // reference to original value for reset
37353         this.originalValue = this.getValue();
37354         this.resetValue =  this.getValue();
37355     },
37356
37357     // private
37358     onFocus : function(){
37359         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37360             this.el.addClass(this.focusClass);
37361         }
37362         if(!this.hasFocus){
37363             this.hasFocus = true;
37364             this.startValue = this.getValue();
37365             this.fireEvent("focus", this);
37366         }
37367     },
37368
37369     beforeBlur : Roo.emptyFn,
37370
37371     // private
37372     onBlur : function(){
37373         this.beforeBlur();
37374         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37375             this.el.removeClass(this.focusClass);
37376         }
37377         this.hasFocus = false;
37378         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37379             this.validate();
37380         }
37381         var v = this.getValue();
37382         if(String(v) !== String(this.startValue)){
37383             this.fireEvent('change', this, v, this.startValue);
37384         }
37385         this.fireEvent("blur", this);
37386     },
37387
37388     /**
37389      * Returns whether or not the field value is currently valid
37390      * @param {Boolean} preventMark True to disable marking the field invalid
37391      * @return {Boolean} True if the value is valid, else false
37392      */
37393     isValid : function(preventMark){
37394         if(this.disabled){
37395             return true;
37396         }
37397         var restore = this.preventMark;
37398         this.preventMark = preventMark === true;
37399         var v = this.validateValue(this.processValue(this.getRawValue()));
37400         this.preventMark = restore;
37401         return v;
37402     },
37403
37404     /**
37405      * Validates the field value
37406      * @return {Boolean} True if the value is valid, else false
37407      */
37408     validate : function(){
37409         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37410             this.clearInvalid();
37411             return true;
37412         }
37413         return false;
37414     },
37415
37416     processValue : function(value){
37417         return value;
37418     },
37419
37420     // private
37421     // Subclasses should provide the validation implementation by overriding this
37422     validateValue : function(value){
37423         return true;
37424     },
37425
37426     /**
37427      * Mark this field as invalid
37428      * @param {String} msg The validation message
37429      */
37430     markInvalid : function(msg){
37431         if(!this.rendered || this.preventMark){ // not rendered
37432             return;
37433         }
37434         
37435         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37436         
37437         obj.el.addClass(this.invalidClass);
37438         msg = msg || this.invalidText;
37439         switch(this.msgTarget){
37440             case 'qtip':
37441                 obj.el.dom.qtip = msg;
37442                 obj.el.dom.qclass = 'x-form-invalid-tip';
37443                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37444                     Roo.QuickTips.enable();
37445                 }
37446                 break;
37447             case 'title':
37448                 this.el.dom.title = msg;
37449                 break;
37450             case 'under':
37451                 if(!this.errorEl){
37452                     var elp = this.el.findParent('.x-form-element', 5, true);
37453                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37454                     this.errorEl.setWidth(elp.getWidth(true)-20);
37455                 }
37456                 this.errorEl.update(msg);
37457                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37458                 break;
37459             case 'side':
37460                 if(!this.errorIcon){
37461                     var elp = this.el.findParent('.x-form-element', 5, true);
37462                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37463                 }
37464                 this.alignErrorIcon();
37465                 this.errorIcon.dom.qtip = msg;
37466                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37467                 this.errorIcon.show();
37468                 this.on('resize', this.alignErrorIcon, this);
37469                 break;
37470             default:
37471                 var t = Roo.getDom(this.msgTarget);
37472                 t.innerHTML = msg;
37473                 t.style.display = this.msgDisplay;
37474                 break;
37475         }
37476         this.fireEvent('invalid', this, msg);
37477     },
37478
37479     // private
37480     alignErrorIcon : function(){
37481         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37482     },
37483
37484     /**
37485      * Clear any invalid styles/messages for this field
37486      */
37487     clearInvalid : function(){
37488         if(!this.rendered || this.preventMark){ // not rendered
37489             return;
37490         }
37491         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37492         
37493         obj.el.removeClass(this.invalidClass);
37494         switch(this.msgTarget){
37495             case 'qtip':
37496                 obj.el.dom.qtip = '';
37497                 break;
37498             case 'title':
37499                 this.el.dom.title = '';
37500                 break;
37501             case 'under':
37502                 if(this.errorEl){
37503                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37504                 }
37505                 break;
37506             case 'side':
37507                 if(this.errorIcon){
37508                     this.errorIcon.dom.qtip = '';
37509                     this.errorIcon.hide();
37510                     this.un('resize', this.alignErrorIcon, this);
37511                 }
37512                 break;
37513             default:
37514                 var t = Roo.getDom(this.msgTarget);
37515                 t.innerHTML = '';
37516                 t.style.display = 'none';
37517                 break;
37518         }
37519         this.fireEvent('valid', this);
37520     },
37521
37522     /**
37523      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37524      * @return {Mixed} value The field value
37525      */
37526     getRawValue : function(){
37527         var v = this.el.getValue();
37528         
37529         return v;
37530     },
37531
37532     /**
37533      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37534      * @return {Mixed} value The field value
37535      */
37536     getValue : function(){
37537         var v = this.el.getValue();
37538          
37539         return v;
37540     },
37541
37542     /**
37543      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37544      * @param {Mixed} value The value to set
37545      */
37546     setRawValue : function(v){
37547         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37548     },
37549
37550     /**
37551      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37552      * @param {Mixed} value The value to set
37553      */
37554     setValue : function(v){
37555         this.value = v;
37556         if(this.rendered){
37557             this.el.dom.value = (v === null || v === undefined ? '' : v);
37558              this.validate();
37559         }
37560     },
37561
37562     adjustSize : function(w, h){
37563         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37564         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37565         return s;
37566     },
37567
37568     adjustWidth : function(tag, w){
37569         tag = tag.toLowerCase();
37570         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37571             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37572                 if(tag == 'input'){
37573                     return w + 2;
37574                 }
37575                 if(tag == 'textarea'){
37576                     return w-2;
37577                 }
37578             }else if(Roo.isOpera){
37579                 if(tag == 'input'){
37580                     return w + 2;
37581                 }
37582                 if(tag == 'textarea'){
37583                     return w-2;
37584                 }
37585             }
37586         }
37587         return w;
37588     }
37589 });
37590
37591
37592 // anything other than normal should be considered experimental
37593 Roo.form.Field.msgFx = {
37594     normal : {
37595         show: function(msgEl, f){
37596             msgEl.setDisplayed('block');
37597         },
37598
37599         hide : function(msgEl, f){
37600             msgEl.setDisplayed(false).update('');
37601         }
37602     },
37603
37604     slide : {
37605         show: function(msgEl, f){
37606             msgEl.slideIn('t', {stopFx:true});
37607         },
37608
37609         hide : function(msgEl, f){
37610             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37611         }
37612     },
37613
37614     slideRight : {
37615         show: function(msgEl, f){
37616             msgEl.fixDisplay();
37617             msgEl.alignTo(f.el, 'tl-tr');
37618             msgEl.slideIn('l', {stopFx:true});
37619         },
37620
37621         hide : function(msgEl, f){
37622             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37623         }
37624     }
37625 };/*
37626  * Based on:
37627  * Ext JS Library 1.1.1
37628  * Copyright(c) 2006-2007, Ext JS, LLC.
37629  *
37630  * Originally Released Under LGPL - original licence link has changed is not relivant.
37631  *
37632  * Fork - LGPL
37633  * <script type="text/javascript">
37634  */
37635  
37636
37637 /**
37638  * @class Roo.form.TextField
37639  * @extends Roo.form.Field
37640  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37641  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37642  * @constructor
37643  * Creates a new TextField
37644  * @param {Object} config Configuration options
37645  */
37646 Roo.form.TextField = function(config){
37647     Roo.form.TextField.superclass.constructor.call(this, config);
37648     this.addEvents({
37649         /**
37650          * @event autosize
37651          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37652          * according to the default logic, but this event provides a hook for the developer to apply additional
37653          * logic at runtime to resize the field if needed.
37654              * @param {Roo.form.Field} this This text field
37655              * @param {Number} width The new field width
37656              */
37657         autosize : true
37658     });
37659 };
37660
37661 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37662     /**
37663      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37664      */
37665     grow : false,
37666     /**
37667      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37668      */
37669     growMin : 30,
37670     /**
37671      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37672      */
37673     growMax : 800,
37674     /**
37675      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37676      */
37677     vtype : null,
37678     /**
37679      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37680      */
37681     maskRe : null,
37682     /**
37683      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37684      */
37685     disableKeyFilter : false,
37686     /**
37687      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37688      */
37689     allowBlank : true,
37690     /**
37691      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37692      */
37693     minLength : 0,
37694     /**
37695      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37696      */
37697     maxLength : Number.MAX_VALUE,
37698     /**
37699      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37700      */
37701     minLengthText : "The minimum length for this field is {0}",
37702     /**
37703      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37704      */
37705     maxLengthText : "The maximum length for this field is {0}",
37706     /**
37707      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37708      */
37709     selectOnFocus : false,
37710     /**
37711      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37712      */
37713     blankText : "This field is required",
37714     /**
37715      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37716      * If available, this function will be called only after the basic validators all return true, and will be passed the
37717      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37718      */
37719     validator : null,
37720     /**
37721      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37722      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37723      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37724      */
37725     regex : null,
37726     /**
37727      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37728      */
37729     regexText : "",
37730     /**
37731      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37732      */
37733     emptyText : null,
37734    
37735
37736     // private
37737     initEvents : function()
37738     {
37739         if (this.emptyText) {
37740             this.el.attr('placeholder', this.emptyText);
37741         }
37742         
37743         Roo.form.TextField.superclass.initEvents.call(this);
37744         if(this.validationEvent == 'keyup'){
37745             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37746             this.el.on('keyup', this.filterValidation, this);
37747         }
37748         else if(this.validationEvent !== false){
37749             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37750         }
37751         
37752         if(this.selectOnFocus){
37753             this.on("focus", this.preFocus, this);
37754             
37755         }
37756         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37757             this.el.on("keypress", this.filterKeys, this);
37758         }
37759         if(this.grow){
37760             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37761             this.el.on("click", this.autoSize,  this);
37762         }
37763         if(this.el.is('input[type=password]') && Roo.isSafari){
37764             this.el.on('keydown', this.SafariOnKeyDown, this);
37765         }
37766     },
37767
37768     processValue : function(value){
37769         if(this.stripCharsRe){
37770             var newValue = value.replace(this.stripCharsRe, '');
37771             if(newValue !== value){
37772                 this.setRawValue(newValue);
37773                 return newValue;
37774             }
37775         }
37776         return value;
37777     },
37778
37779     filterValidation : function(e){
37780         if(!e.isNavKeyPress()){
37781             this.validationTask.delay(this.validationDelay);
37782         }
37783     },
37784
37785     // private
37786     onKeyUp : function(e){
37787         if(!e.isNavKeyPress()){
37788             this.autoSize();
37789         }
37790     },
37791
37792     /**
37793      * Resets the current field value to the originally-loaded value and clears any validation messages.
37794      *  
37795      */
37796     reset : function(){
37797         Roo.form.TextField.superclass.reset.call(this);
37798        
37799     },
37800
37801     
37802     // private
37803     preFocus : function(){
37804         
37805         if(this.selectOnFocus){
37806             this.el.dom.select();
37807         }
37808     },
37809
37810     
37811     // private
37812     filterKeys : function(e){
37813         var k = e.getKey();
37814         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37815             return;
37816         }
37817         var c = e.getCharCode(), cc = String.fromCharCode(c);
37818         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37819             return;
37820         }
37821         if(!this.maskRe.test(cc)){
37822             e.stopEvent();
37823         }
37824     },
37825
37826     setValue : function(v){
37827         
37828         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37829         
37830         this.autoSize();
37831     },
37832
37833     /**
37834      * Validates a value according to the field's validation rules and marks the field as invalid
37835      * if the validation fails
37836      * @param {Mixed} value The value to validate
37837      * @return {Boolean} True if the value is valid, else false
37838      */
37839     validateValue : function(value){
37840         if(value.length < 1)  { // if it's blank
37841              if(this.allowBlank){
37842                 this.clearInvalid();
37843                 return true;
37844              }else{
37845                 this.markInvalid(this.blankText);
37846                 return false;
37847              }
37848         }
37849         if(value.length < this.minLength){
37850             this.markInvalid(String.format(this.minLengthText, this.minLength));
37851             return false;
37852         }
37853         if(value.length > this.maxLength){
37854             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37855             return false;
37856         }
37857         if(this.vtype){
37858             var vt = Roo.form.VTypes;
37859             if(!vt[this.vtype](value, this)){
37860                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37861                 return false;
37862             }
37863         }
37864         if(typeof this.validator == "function"){
37865             var msg = this.validator(value);
37866             if(msg !== true){
37867                 this.markInvalid(msg);
37868                 return false;
37869             }
37870         }
37871         if(this.regex && !this.regex.test(value)){
37872             this.markInvalid(this.regexText);
37873             return false;
37874         }
37875         return true;
37876     },
37877
37878     /**
37879      * Selects text in this field
37880      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37881      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37882      */
37883     selectText : function(start, end){
37884         var v = this.getRawValue();
37885         if(v.length > 0){
37886             start = start === undefined ? 0 : start;
37887             end = end === undefined ? v.length : end;
37888             var d = this.el.dom;
37889             if(d.setSelectionRange){
37890                 d.setSelectionRange(start, end);
37891             }else if(d.createTextRange){
37892                 var range = d.createTextRange();
37893                 range.moveStart("character", start);
37894                 range.moveEnd("character", v.length-end);
37895                 range.select();
37896             }
37897         }
37898     },
37899
37900     /**
37901      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37902      * This only takes effect if grow = true, and fires the autosize event.
37903      */
37904     autoSize : function(){
37905         if(!this.grow || !this.rendered){
37906             return;
37907         }
37908         if(!this.metrics){
37909             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37910         }
37911         var el = this.el;
37912         var v = el.dom.value;
37913         var d = document.createElement('div');
37914         d.appendChild(document.createTextNode(v));
37915         v = d.innerHTML;
37916         d = null;
37917         v += "&#160;";
37918         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37919         this.el.setWidth(w);
37920         this.fireEvent("autosize", this, w);
37921     },
37922     
37923     // private
37924     SafariOnKeyDown : function(event)
37925     {
37926         // this is a workaround for a password hang bug on chrome/ webkit.
37927         
37928         var isSelectAll = false;
37929         
37930         if(this.el.dom.selectionEnd > 0){
37931             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37932         }
37933         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37934             event.preventDefault();
37935             this.setValue('');
37936             return;
37937         }
37938         
37939         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37940             
37941             event.preventDefault();
37942             // this is very hacky as keydown always get's upper case.
37943             
37944             var cc = String.fromCharCode(event.getCharCode());
37945             
37946             
37947             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37948             
37949         }
37950         
37951         
37952     }
37953 });/*
37954  * Based on:
37955  * Ext JS Library 1.1.1
37956  * Copyright(c) 2006-2007, Ext JS, LLC.
37957  *
37958  * Originally Released Under LGPL - original licence link has changed is not relivant.
37959  *
37960  * Fork - LGPL
37961  * <script type="text/javascript">
37962  */
37963  
37964 /**
37965  * @class Roo.form.Hidden
37966  * @extends Roo.form.TextField
37967  * Simple Hidden element used on forms 
37968  * 
37969  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37970  * 
37971  * @constructor
37972  * Creates a new Hidden form element.
37973  * @param {Object} config Configuration options
37974  */
37975
37976
37977
37978 // easy hidden field...
37979 Roo.form.Hidden = function(config){
37980     Roo.form.Hidden.superclass.constructor.call(this, config);
37981 };
37982   
37983 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37984     fieldLabel:      '',
37985     inputType:      'hidden',
37986     width:          50,
37987     allowBlank:     true,
37988     labelSeparator: '',
37989     hidden:         true,
37990     itemCls :       'x-form-item-display-none'
37991
37992
37993 });
37994
37995
37996 /*
37997  * Based on:
37998  * Ext JS Library 1.1.1
37999  * Copyright(c) 2006-2007, Ext JS, LLC.
38000  *
38001  * Originally Released Under LGPL - original licence link has changed is not relivant.
38002  *
38003  * Fork - LGPL
38004  * <script type="text/javascript">
38005  */
38006  
38007 /**
38008  * @class Roo.form.TriggerField
38009  * @extends Roo.form.TextField
38010  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
38011  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
38012  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
38013  * for which you can provide a custom implementation.  For example:
38014  * <pre><code>
38015 var trigger = new Roo.form.TriggerField();
38016 trigger.onTriggerClick = myTriggerFn;
38017 trigger.applyTo('my-field');
38018 </code></pre>
38019  *
38020  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
38021  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
38022  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38023  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
38024  * @constructor
38025  * Create a new TriggerField.
38026  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
38027  * to the base TextField)
38028  */
38029 Roo.form.TriggerField = function(config){
38030     this.mimicing = false;
38031     Roo.form.TriggerField.superclass.constructor.call(this, config);
38032 };
38033
38034 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
38035     /**
38036      * @cfg {String} triggerClass A CSS class to apply to the trigger
38037      */
38038     /**
38039      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38040      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
38041      */
38042     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38043     /**
38044      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38045      */
38046     hideTrigger:false,
38047
38048     /** @cfg {Boolean} grow @hide */
38049     /** @cfg {Number} growMin @hide */
38050     /** @cfg {Number} growMax @hide */
38051
38052     /**
38053      * @hide 
38054      * @method
38055      */
38056     autoSize: Roo.emptyFn,
38057     // private
38058     monitorTab : true,
38059     // private
38060     deferHeight : true,
38061
38062     
38063     actionMode : 'wrap',
38064     // private
38065     onResize : function(w, h){
38066         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38067         if(typeof w == 'number'){
38068             var x = w - this.trigger.getWidth();
38069             this.el.setWidth(this.adjustWidth('input', x));
38070             this.trigger.setStyle('left', x+'px');
38071         }
38072     },
38073
38074     // private
38075     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38076
38077     // private
38078     getResizeEl : function(){
38079         return this.wrap;
38080     },
38081
38082     // private
38083     getPositionEl : function(){
38084         return this.wrap;
38085     },
38086
38087     // private
38088     alignErrorIcon : function(){
38089         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38090     },
38091
38092     // private
38093     onRender : function(ct, position){
38094         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38095         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38096         this.trigger = this.wrap.createChild(this.triggerConfig ||
38097                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38098         if(this.hideTrigger){
38099             this.trigger.setDisplayed(false);
38100         }
38101         this.initTrigger();
38102         if(!this.width){
38103             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38104         }
38105     },
38106
38107     // private
38108     initTrigger : function(){
38109         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38110         this.trigger.addClassOnOver('x-form-trigger-over');
38111         this.trigger.addClassOnClick('x-form-trigger-click');
38112     },
38113
38114     // private
38115     onDestroy : function(){
38116         if(this.trigger){
38117             this.trigger.removeAllListeners();
38118             this.trigger.remove();
38119         }
38120         if(this.wrap){
38121             this.wrap.remove();
38122         }
38123         Roo.form.TriggerField.superclass.onDestroy.call(this);
38124     },
38125
38126     // private
38127     onFocus : function(){
38128         Roo.form.TriggerField.superclass.onFocus.call(this);
38129         if(!this.mimicing){
38130             this.wrap.addClass('x-trigger-wrap-focus');
38131             this.mimicing = true;
38132             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38133             if(this.monitorTab){
38134                 this.el.on("keydown", this.checkTab, this);
38135             }
38136         }
38137     },
38138
38139     // private
38140     checkTab : function(e){
38141         if(e.getKey() == e.TAB){
38142             this.triggerBlur();
38143         }
38144     },
38145
38146     // private
38147     onBlur : function(){
38148         // do nothing
38149     },
38150
38151     // private
38152     mimicBlur : function(e, t){
38153         if(!this.wrap.contains(t) && this.validateBlur()){
38154             this.triggerBlur();
38155         }
38156     },
38157
38158     // private
38159     triggerBlur : function(){
38160         this.mimicing = false;
38161         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38162         if(this.monitorTab){
38163             this.el.un("keydown", this.checkTab, this);
38164         }
38165         this.wrap.removeClass('x-trigger-wrap-focus');
38166         Roo.form.TriggerField.superclass.onBlur.call(this);
38167     },
38168
38169     // private
38170     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38171     validateBlur : function(e, t){
38172         return true;
38173     },
38174
38175     // private
38176     onDisable : function(){
38177         Roo.form.TriggerField.superclass.onDisable.call(this);
38178         if(this.wrap){
38179             this.wrap.addClass('x-item-disabled');
38180         }
38181     },
38182
38183     // private
38184     onEnable : function(){
38185         Roo.form.TriggerField.superclass.onEnable.call(this);
38186         if(this.wrap){
38187             this.wrap.removeClass('x-item-disabled');
38188         }
38189     },
38190
38191     // private
38192     onShow : function(){
38193         var ae = this.getActionEl();
38194         
38195         if(ae){
38196             ae.dom.style.display = '';
38197             ae.dom.style.visibility = 'visible';
38198         }
38199     },
38200
38201     // private
38202     
38203     onHide : function(){
38204         var ae = this.getActionEl();
38205         ae.dom.style.display = 'none';
38206     },
38207
38208     /**
38209      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38210      * by an implementing function.
38211      * @method
38212      * @param {EventObject} e
38213      */
38214     onTriggerClick : Roo.emptyFn
38215 });
38216
38217 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38218 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38219 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38220 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38221     initComponent : function(){
38222         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38223
38224         this.triggerConfig = {
38225             tag:'span', cls:'x-form-twin-triggers', cn:[
38226             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38227             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38228         ]};
38229     },
38230
38231     getTrigger : function(index){
38232         return this.triggers[index];
38233     },
38234
38235     initTrigger : function(){
38236         var ts = this.trigger.select('.x-form-trigger', true);
38237         this.wrap.setStyle('overflow', 'hidden');
38238         var triggerField = this;
38239         ts.each(function(t, all, index){
38240             t.hide = function(){
38241                 var w = triggerField.wrap.getWidth();
38242                 this.dom.style.display = 'none';
38243                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38244             };
38245             t.show = function(){
38246                 var w = triggerField.wrap.getWidth();
38247                 this.dom.style.display = '';
38248                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38249             };
38250             var triggerIndex = 'Trigger'+(index+1);
38251
38252             if(this['hide'+triggerIndex]){
38253                 t.dom.style.display = 'none';
38254             }
38255             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38256             t.addClassOnOver('x-form-trigger-over');
38257             t.addClassOnClick('x-form-trigger-click');
38258         }, this);
38259         this.triggers = ts.elements;
38260     },
38261
38262     onTrigger1Click : Roo.emptyFn,
38263     onTrigger2Click : Roo.emptyFn
38264 });/*
38265  * Based on:
38266  * Ext JS Library 1.1.1
38267  * Copyright(c) 2006-2007, Ext JS, LLC.
38268  *
38269  * Originally Released Under LGPL - original licence link has changed is not relivant.
38270  *
38271  * Fork - LGPL
38272  * <script type="text/javascript">
38273  */
38274  
38275 /**
38276  * @class Roo.form.TextArea
38277  * @extends Roo.form.TextField
38278  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38279  * support for auto-sizing.
38280  * @constructor
38281  * Creates a new TextArea
38282  * @param {Object} config Configuration options
38283  */
38284 Roo.form.TextArea = function(config){
38285     Roo.form.TextArea.superclass.constructor.call(this, config);
38286     // these are provided exchanges for backwards compat
38287     // minHeight/maxHeight were replaced by growMin/growMax to be
38288     // compatible with TextField growing config values
38289     if(this.minHeight !== undefined){
38290         this.growMin = this.minHeight;
38291     }
38292     if(this.maxHeight !== undefined){
38293         this.growMax = this.maxHeight;
38294     }
38295 };
38296
38297 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38298     /**
38299      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38300      */
38301     growMin : 60,
38302     /**
38303      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38304      */
38305     growMax: 1000,
38306     /**
38307      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38308      * in the field (equivalent to setting overflow: hidden, defaults to false)
38309      */
38310     preventScrollbars: false,
38311     /**
38312      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38313      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38314      */
38315
38316     // private
38317     onRender : function(ct, position){
38318         if(!this.el){
38319             this.defaultAutoCreate = {
38320                 tag: "textarea",
38321                 style:"width:300px;height:60px;",
38322                 autocomplete: "new-password"
38323             };
38324         }
38325         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38326         if(this.grow){
38327             this.textSizeEl = Roo.DomHelper.append(document.body, {
38328                 tag: "pre", cls: "x-form-grow-sizer"
38329             });
38330             if(this.preventScrollbars){
38331                 this.el.setStyle("overflow", "hidden");
38332             }
38333             this.el.setHeight(this.growMin);
38334         }
38335     },
38336
38337     onDestroy : function(){
38338         if(this.textSizeEl){
38339             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38340         }
38341         Roo.form.TextArea.superclass.onDestroy.call(this);
38342     },
38343
38344     // private
38345     onKeyUp : function(e){
38346         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38347             this.autoSize();
38348         }
38349     },
38350
38351     /**
38352      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38353      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38354      */
38355     autoSize : function(){
38356         if(!this.grow || !this.textSizeEl){
38357             return;
38358         }
38359         var el = this.el;
38360         var v = el.dom.value;
38361         var ts = this.textSizeEl;
38362
38363         ts.innerHTML = '';
38364         ts.appendChild(document.createTextNode(v));
38365         v = ts.innerHTML;
38366
38367         Roo.fly(ts).setWidth(this.el.getWidth());
38368         if(v.length < 1){
38369             v = "&#160;&#160;";
38370         }else{
38371             if(Roo.isIE){
38372                 v = v.replace(/\n/g, '<p>&#160;</p>');
38373             }
38374             v += "&#160;\n&#160;";
38375         }
38376         ts.innerHTML = v;
38377         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38378         if(h != this.lastHeight){
38379             this.lastHeight = h;
38380             this.el.setHeight(h);
38381             this.fireEvent("autosize", this, h);
38382         }
38383     }
38384 });/*
38385  * Based on:
38386  * Ext JS Library 1.1.1
38387  * Copyright(c) 2006-2007, Ext JS, LLC.
38388  *
38389  * Originally Released Under LGPL - original licence link has changed is not relivant.
38390  *
38391  * Fork - LGPL
38392  * <script type="text/javascript">
38393  */
38394  
38395
38396 /**
38397  * @class Roo.form.NumberField
38398  * @extends Roo.form.TextField
38399  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38400  * @constructor
38401  * Creates a new NumberField
38402  * @param {Object} config Configuration options
38403  */
38404 Roo.form.NumberField = function(config){
38405     Roo.form.NumberField.superclass.constructor.call(this, config);
38406 };
38407
38408 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38409     /**
38410      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38411      */
38412     fieldClass: "x-form-field x-form-num-field",
38413     /**
38414      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38415      */
38416     allowDecimals : true,
38417     /**
38418      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38419      */
38420     decimalSeparator : ".",
38421     /**
38422      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38423      */
38424     decimalPrecision : 2,
38425     /**
38426      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38427      */
38428     allowNegative : true,
38429     /**
38430      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38431      */
38432     minValue : Number.NEGATIVE_INFINITY,
38433     /**
38434      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38435      */
38436     maxValue : Number.MAX_VALUE,
38437     /**
38438      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38439      */
38440     minText : "The minimum value for this field is {0}",
38441     /**
38442      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38443      */
38444     maxText : "The maximum value for this field is {0}",
38445     /**
38446      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38447      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38448      */
38449     nanText : "{0} is not a valid number",
38450
38451     // private
38452     initEvents : function(){
38453         Roo.form.NumberField.superclass.initEvents.call(this);
38454         var allowed = "0123456789";
38455         if(this.allowDecimals){
38456             allowed += this.decimalSeparator;
38457         }
38458         if(this.allowNegative){
38459             allowed += "-";
38460         }
38461         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38462         var keyPress = function(e){
38463             var k = e.getKey();
38464             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38465                 return;
38466             }
38467             var c = e.getCharCode();
38468             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38469                 e.stopEvent();
38470             }
38471         };
38472         this.el.on("keypress", keyPress, this);
38473     },
38474
38475     // private
38476     validateValue : function(value){
38477         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38478             return false;
38479         }
38480         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38481              return true;
38482         }
38483         var num = this.parseValue(value);
38484         if(isNaN(num)){
38485             this.markInvalid(String.format(this.nanText, value));
38486             return false;
38487         }
38488         if(num < this.minValue){
38489             this.markInvalid(String.format(this.minText, this.minValue));
38490             return false;
38491         }
38492         if(num > this.maxValue){
38493             this.markInvalid(String.format(this.maxText, this.maxValue));
38494             return false;
38495         }
38496         return true;
38497     },
38498
38499     getValue : function(){
38500         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38501     },
38502
38503     // private
38504     parseValue : function(value){
38505         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38506         return isNaN(value) ? '' : value;
38507     },
38508
38509     // private
38510     fixPrecision : function(value){
38511         var nan = isNaN(value);
38512         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38513             return nan ? '' : value;
38514         }
38515         return parseFloat(value).toFixed(this.decimalPrecision);
38516     },
38517
38518     setValue : function(v){
38519         v = this.fixPrecision(v);
38520         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38521     },
38522
38523     // private
38524     decimalPrecisionFcn : function(v){
38525         return Math.floor(v);
38526     },
38527
38528     beforeBlur : function(){
38529         var v = this.parseValue(this.getRawValue());
38530         if(v){
38531             this.setValue(v);
38532         }
38533     }
38534 });/*
38535  * Based on:
38536  * Ext JS Library 1.1.1
38537  * Copyright(c) 2006-2007, Ext JS, LLC.
38538  *
38539  * Originally Released Under LGPL - original licence link has changed is not relivant.
38540  *
38541  * Fork - LGPL
38542  * <script type="text/javascript">
38543  */
38544  
38545 /**
38546  * @class Roo.form.DateField
38547  * @extends Roo.form.TriggerField
38548  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38549 * @constructor
38550 * Create a new DateField
38551 * @param {Object} config
38552  */
38553 Roo.form.DateField = function(config){
38554     Roo.form.DateField.superclass.constructor.call(this, config);
38555     
38556       this.addEvents({
38557          
38558         /**
38559          * @event select
38560          * Fires when a date is selected
38561              * @param {Roo.form.DateField} combo This combo box
38562              * @param {Date} date The date selected
38563              */
38564         'select' : true
38565          
38566     });
38567     
38568     
38569     if(typeof this.minValue == "string") {
38570         this.minValue = this.parseDate(this.minValue);
38571     }
38572     if(typeof this.maxValue == "string") {
38573         this.maxValue = this.parseDate(this.maxValue);
38574     }
38575     this.ddMatch = null;
38576     if(this.disabledDates){
38577         var dd = this.disabledDates;
38578         var re = "(?:";
38579         for(var i = 0; i < dd.length; i++){
38580             re += dd[i];
38581             if(i != dd.length-1) {
38582                 re += "|";
38583             }
38584         }
38585         this.ddMatch = new RegExp(re + ")");
38586     }
38587 };
38588
38589 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38590     /**
38591      * @cfg {String} format
38592      * The default date format string which can be overriden for localization support.  The format must be
38593      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38594      */
38595     format : "m/d/y",
38596     /**
38597      * @cfg {String} altFormats
38598      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38599      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38600      */
38601     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38602     /**
38603      * @cfg {Array} disabledDays
38604      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38605      */
38606     disabledDays : null,
38607     /**
38608      * @cfg {String} disabledDaysText
38609      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38610      */
38611     disabledDaysText : "Disabled",
38612     /**
38613      * @cfg {Array} disabledDates
38614      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38615      * expression so they are very powerful. Some examples:
38616      * <ul>
38617      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38618      * <li>["03/08", "09/16"] would disable those days for every year</li>
38619      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38620      * <li>["03/../2006"] would disable every day in March 2006</li>
38621      * <li>["^03"] would disable every day in every March</li>
38622      * </ul>
38623      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38624      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38625      */
38626     disabledDates : null,
38627     /**
38628      * @cfg {String} disabledDatesText
38629      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38630      */
38631     disabledDatesText : "Disabled",
38632     /**
38633      * @cfg {Date/String} minValue
38634      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38635      * valid format (defaults to null).
38636      */
38637     minValue : null,
38638     /**
38639      * @cfg {Date/String} maxValue
38640      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38641      * valid format (defaults to null).
38642      */
38643     maxValue : null,
38644     /**
38645      * @cfg {String} minText
38646      * The error text to display when the date in the cell is before minValue (defaults to
38647      * 'The date in this field must be after {minValue}').
38648      */
38649     minText : "The date in this field must be equal to or after {0}",
38650     /**
38651      * @cfg {String} maxText
38652      * The error text to display when the date in the cell is after maxValue (defaults to
38653      * 'The date in this field must be before {maxValue}').
38654      */
38655     maxText : "The date in this field must be equal to or before {0}",
38656     /**
38657      * @cfg {String} invalidText
38658      * The error text to display when the date in the field is invalid (defaults to
38659      * '{value} is not a valid date - it must be in the format {format}').
38660      */
38661     invalidText : "{0} is not a valid date - it must be in the format {1}",
38662     /**
38663      * @cfg {String} triggerClass
38664      * An additional CSS class used to style the trigger button.  The trigger will always get the
38665      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38666      * which displays a calendar icon).
38667      */
38668     triggerClass : 'x-form-date-trigger',
38669     
38670
38671     /**
38672      * @cfg {Boolean} useIso
38673      * if enabled, then the date field will use a hidden field to store the 
38674      * real value as iso formated date. default (false)
38675      */ 
38676     useIso : false,
38677     /**
38678      * @cfg {String/Object} autoCreate
38679      * A DomHelper element spec, or true for a default element spec (defaults to
38680      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38681      */ 
38682     // private
38683     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38684     
38685     // private
38686     hiddenField: false,
38687     
38688     onRender : function(ct, position)
38689     {
38690         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38691         if (this.useIso) {
38692             //this.el.dom.removeAttribute('name'); 
38693             Roo.log("Changing name?");
38694             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38695             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38696                     'before', true);
38697             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38698             // prevent input submission
38699             this.hiddenName = this.name;
38700         }
38701             
38702             
38703     },
38704     
38705     // private
38706     validateValue : function(value)
38707     {
38708         value = this.formatDate(value);
38709         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38710             Roo.log('super failed');
38711             return false;
38712         }
38713         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38714              return true;
38715         }
38716         var svalue = value;
38717         value = this.parseDate(value);
38718         if(!value){
38719             Roo.log('parse date failed' + svalue);
38720             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38721             return false;
38722         }
38723         var time = value.getTime();
38724         if(this.minValue && time < this.minValue.getTime()){
38725             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38726             return false;
38727         }
38728         if(this.maxValue && time > this.maxValue.getTime()){
38729             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38730             return false;
38731         }
38732         if(this.disabledDays){
38733             var day = value.getDay();
38734             for(var i = 0; i < this.disabledDays.length; i++) {
38735                 if(day === this.disabledDays[i]){
38736                     this.markInvalid(this.disabledDaysText);
38737                     return false;
38738                 }
38739             }
38740         }
38741         var fvalue = this.formatDate(value);
38742         if(this.ddMatch && this.ddMatch.test(fvalue)){
38743             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38744             return false;
38745         }
38746         return true;
38747     },
38748
38749     // private
38750     // Provides logic to override the default TriggerField.validateBlur which just returns true
38751     validateBlur : function(){
38752         return !this.menu || !this.menu.isVisible();
38753     },
38754     
38755     getName: function()
38756     {
38757         // returns hidden if it's set..
38758         if (!this.rendered) {return ''};
38759         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38760         
38761     },
38762
38763     /**
38764      * Returns the current date value of the date field.
38765      * @return {Date} The date value
38766      */
38767     getValue : function(){
38768         
38769         return  this.hiddenField ?
38770                 this.hiddenField.value :
38771                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38772     },
38773
38774     /**
38775      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38776      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38777      * (the default format used is "m/d/y").
38778      * <br />Usage:
38779      * <pre><code>
38780 //All of these calls set the same date value (May 4, 2006)
38781
38782 //Pass a date object:
38783 var dt = new Date('5/4/06');
38784 dateField.setValue(dt);
38785
38786 //Pass a date string (default format):
38787 dateField.setValue('5/4/06');
38788
38789 //Pass a date string (custom format):
38790 dateField.format = 'Y-m-d';
38791 dateField.setValue('2006-5-4');
38792 </code></pre>
38793      * @param {String/Date} date The date or valid date string
38794      */
38795     setValue : function(date){
38796         if (this.hiddenField) {
38797             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38798         }
38799         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38800         // make sure the value field is always stored as a date..
38801         this.value = this.parseDate(date);
38802         
38803         
38804     },
38805
38806     // private
38807     parseDate : function(value){
38808         if(!value || value instanceof Date){
38809             return value;
38810         }
38811         var v = Date.parseDate(value, this.format);
38812          if (!v && this.useIso) {
38813             v = Date.parseDate(value, 'Y-m-d');
38814         }
38815         if(!v && this.altFormats){
38816             if(!this.altFormatsArray){
38817                 this.altFormatsArray = this.altFormats.split("|");
38818             }
38819             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38820                 v = Date.parseDate(value, this.altFormatsArray[i]);
38821             }
38822         }
38823         return v;
38824     },
38825
38826     // private
38827     formatDate : function(date, fmt){
38828         return (!date || !(date instanceof Date)) ?
38829                date : date.dateFormat(fmt || this.format);
38830     },
38831
38832     // private
38833     menuListeners : {
38834         select: function(m, d){
38835             
38836             this.setValue(d);
38837             this.fireEvent('select', this, d);
38838         },
38839         show : function(){ // retain focus styling
38840             this.onFocus();
38841         },
38842         hide : function(){
38843             this.focus.defer(10, this);
38844             var ml = this.menuListeners;
38845             this.menu.un("select", ml.select,  this);
38846             this.menu.un("show", ml.show,  this);
38847             this.menu.un("hide", ml.hide,  this);
38848         }
38849     },
38850
38851     // private
38852     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38853     onTriggerClick : function(){
38854         if(this.disabled){
38855             return;
38856         }
38857         if(this.menu == null){
38858             this.menu = new Roo.menu.DateMenu();
38859         }
38860         Roo.apply(this.menu.picker,  {
38861             showClear: this.allowBlank,
38862             minDate : this.minValue,
38863             maxDate : this.maxValue,
38864             disabledDatesRE : this.ddMatch,
38865             disabledDatesText : this.disabledDatesText,
38866             disabledDays : this.disabledDays,
38867             disabledDaysText : this.disabledDaysText,
38868             format : this.useIso ? 'Y-m-d' : this.format,
38869             minText : String.format(this.minText, this.formatDate(this.minValue)),
38870             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38871         });
38872         this.menu.on(Roo.apply({}, this.menuListeners, {
38873             scope:this
38874         }));
38875         this.menu.picker.setValue(this.getValue() || new Date());
38876         this.menu.show(this.el, "tl-bl?");
38877     },
38878
38879     beforeBlur : function(){
38880         var v = this.parseDate(this.getRawValue());
38881         if(v){
38882             this.setValue(v);
38883         }
38884     },
38885
38886     /*@
38887      * overide
38888      * 
38889      */
38890     isDirty : function() {
38891         if(this.disabled) {
38892             return false;
38893         }
38894         
38895         if(typeof(this.startValue) === 'undefined'){
38896             return false;
38897         }
38898         
38899         return String(this.getValue()) !== String(this.startValue);
38900         
38901     }
38902 });/*
38903  * Based on:
38904  * Ext JS Library 1.1.1
38905  * Copyright(c) 2006-2007, Ext JS, LLC.
38906  *
38907  * Originally Released Under LGPL - original licence link has changed is not relivant.
38908  *
38909  * Fork - LGPL
38910  * <script type="text/javascript">
38911  */
38912  
38913 /**
38914  * @class Roo.form.MonthField
38915  * @extends Roo.form.TriggerField
38916  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38917 * @constructor
38918 * Create a new MonthField
38919 * @param {Object} config
38920  */
38921 Roo.form.MonthField = function(config){
38922     
38923     Roo.form.MonthField.superclass.constructor.call(this, config);
38924     
38925       this.addEvents({
38926          
38927         /**
38928          * @event select
38929          * Fires when a date is selected
38930              * @param {Roo.form.MonthFieeld} combo This combo box
38931              * @param {Date} date The date selected
38932              */
38933         'select' : true
38934          
38935     });
38936     
38937     
38938     if(typeof this.minValue == "string") {
38939         this.minValue = this.parseDate(this.minValue);
38940     }
38941     if(typeof this.maxValue == "string") {
38942         this.maxValue = this.parseDate(this.maxValue);
38943     }
38944     this.ddMatch = null;
38945     if(this.disabledDates){
38946         var dd = this.disabledDates;
38947         var re = "(?:";
38948         for(var i = 0; i < dd.length; i++){
38949             re += dd[i];
38950             if(i != dd.length-1) {
38951                 re += "|";
38952             }
38953         }
38954         this.ddMatch = new RegExp(re + ")");
38955     }
38956 };
38957
38958 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38959     /**
38960      * @cfg {String} format
38961      * The default date format string which can be overriden for localization support.  The format must be
38962      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38963      */
38964     format : "M Y",
38965     /**
38966      * @cfg {String} altFormats
38967      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38968      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38969      */
38970     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38971     /**
38972      * @cfg {Array} disabledDays
38973      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38974      */
38975     disabledDays : [0,1,2,3,4,5,6],
38976     /**
38977      * @cfg {String} disabledDaysText
38978      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38979      */
38980     disabledDaysText : "Disabled",
38981     /**
38982      * @cfg {Array} disabledDates
38983      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38984      * expression so they are very powerful. Some examples:
38985      * <ul>
38986      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38987      * <li>["03/08", "09/16"] would disable those days for every year</li>
38988      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38989      * <li>["03/../2006"] would disable every day in March 2006</li>
38990      * <li>["^03"] would disable every day in every March</li>
38991      * </ul>
38992      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38993      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38994      */
38995     disabledDates : null,
38996     /**
38997      * @cfg {String} disabledDatesText
38998      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38999      */
39000     disabledDatesText : "Disabled",
39001     /**
39002      * @cfg {Date/String} minValue
39003      * The minimum allowed date. Can be either a Javascript date object or a string date in a
39004      * valid format (defaults to null).
39005      */
39006     minValue : null,
39007     /**
39008      * @cfg {Date/String} maxValue
39009      * The maximum allowed date. Can be either a Javascript date object or a string date in a
39010      * valid format (defaults to null).
39011      */
39012     maxValue : null,
39013     /**
39014      * @cfg {String} minText
39015      * The error text to display when the date in the cell is before minValue (defaults to
39016      * 'The date in this field must be after {minValue}').
39017      */
39018     minText : "The date in this field must be equal to or after {0}",
39019     /**
39020      * @cfg {String} maxTextf
39021      * The error text to display when the date in the cell is after maxValue (defaults to
39022      * 'The date in this field must be before {maxValue}').
39023      */
39024     maxText : "The date in this field must be equal to or before {0}",
39025     /**
39026      * @cfg {String} invalidText
39027      * The error text to display when the date in the field is invalid (defaults to
39028      * '{value} is not a valid date - it must be in the format {format}').
39029      */
39030     invalidText : "{0} is not a valid date - it must be in the format {1}",
39031     /**
39032      * @cfg {String} triggerClass
39033      * An additional CSS class used to style the trigger button.  The trigger will always get the
39034      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
39035      * which displays a calendar icon).
39036      */
39037     triggerClass : 'x-form-date-trigger',
39038     
39039
39040     /**
39041      * @cfg {Boolean} useIso
39042      * if enabled, then the date field will use a hidden field to store the 
39043      * real value as iso formated date. default (true)
39044      */ 
39045     useIso : true,
39046     /**
39047      * @cfg {String/Object} autoCreate
39048      * A DomHelper element spec, or true for a default element spec (defaults to
39049      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
39050      */ 
39051     // private
39052     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
39053     
39054     // private
39055     hiddenField: false,
39056     
39057     hideMonthPicker : false,
39058     
39059     onRender : function(ct, position)
39060     {
39061         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39062         if (this.useIso) {
39063             this.el.dom.removeAttribute('name'); 
39064             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39065                     'before', true);
39066             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39067             // prevent input submission
39068             this.hiddenName = this.name;
39069         }
39070             
39071             
39072     },
39073     
39074     // private
39075     validateValue : function(value)
39076     {
39077         value = this.formatDate(value);
39078         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39079             return false;
39080         }
39081         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39082              return true;
39083         }
39084         var svalue = value;
39085         value = this.parseDate(value);
39086         if(!value){
39087             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39088             return false;
39089         }
39090         var time = value.getTime();
39091         if(this.minValue && time < this.minValue.getTime()){
39092             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39093             return false;
39094         }
39095         if(this.maxValue && time > this.maxValue.getTime()){
39096             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39097             return false;
39098         }
39099         /*if(this.disabledDays){
39100             var day = value.getDay();
39101             for(var i = 0; i < this.disabledDays.length; i++) {
39102                 if(day === this.disabledDays[i]){
39103                     this.markInvalid(this.disabledDaysText);
39104                     return false;
39105                 }
39106             }
39107         }
39108         */
39109         var fvalue = this.formatDate(value);
39110         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39111             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39112             return false;
39113         }
39114         */
39115         return true;
39116     },
39117
39118     // private
39119     // Provides logic to override the default TriggerField.validateBlur which just returns true
39120     validateBlur : function(){
39121         return !this.menu || !this.menu.isVisible();
39122     },
39123
39124     /**
39125      * Returns the current date value of the date field.
39126      * @return {Date} The date value
39127      */
39128     getValue : function(){
39129         
39130         
39131         
39132         return  this.hiddenField ?
39133                 this.hiddenField.value :
39134                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39135     },
39136
39137     /**
39138      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39139      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39140      * (the default format used is "m/d/y").
39141      * <br />Usage:
39142      * <pre><code>
39143 //All of these calls set the same date value (May 4, 2006)
39144
39145 //Pass a date object:
39146 var dt = new Date('5/4/06');
39147 monthField.setValue(dt);
39148
39149 //Pass a date string (default format):
39150 monthField.setValue('5/4/06');
39151
39152 //Pass a date string (custom format):
39153 monthField.format = 'Y-m-d';
39154 monthField.setValue('2006-5-4');
39155 </code></pre>
39156      * @param {String/Date} date The date or valid date string
39157      */
39158     setValue : function(date){
39159         Roo.log('month setValue' + date);
39160         // can only be first of month..
39161         
39162         var val = this.parseDate(date);
39163         
39164         if (this.hiddenField) {
39165             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39166         }
39167         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39168         this.value = this.parseDate(date);
39169     },
39170
39171     // private
39172     parseDate : function(value){
39173         if(!value || value instanceof Date){
39174             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39175             return value;
39176         }
39177         var v = Date.parseDate(value, this.format);
39178         if (!v && this.useIso) {
39179             v = Date.parseDate(value, 'Y-m-d');
39180         }
39181         if (v) {
39182             // 
39183             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39184         }
39185         
39186         
39187         if(!v && this.altFormats){
39188             if(!this.altFormatsArray){
39189                 this.altFormatsArray = this.altFormats.split("|");
39190             }
39191             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39192                 v = Date.parseDate(value, this.altFormatsArray[i]);
39193             }
39194         }
39195         return v;
39196     },
39197
39198     // private
39199     formatDate : function(date, fmt){
39200         return (!date || !(date instanceof Date)) ?
39201                date : date.dateFormat(fmt || this.format);
39202     },
39203
39204     // private
39205     menuListeners : {
39206         select: function(m, d){
39207             this.setValue(d);
39208             this.fireEvent('select', this, d);
39209         },
39210         show : function(){ // retain focus styling
39211             this.onFocus();
39212         },
39213         hide : function(){
39214             this.focus.defer(10, this);
39215             var ml = this.menuListeners;
39216             this.menu.un("select", ml.select,  this);
39217             this.menu.un("show", ml.show,  this);
39218             this.menu.un("hide", ml.hide,  this);
39219         }
39220     },
39221     // private
39222     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39223     onTriggerClick : function(){
39224         if(this.disabled){
39225             return;
39226         }
39227         if(this.menu == null){
39228             this.menu = new Roo.menu.DateMenu();
39229            
39230         }
39231         
39232         Roo.apply(this.menu.picker,  {
39233             
39234             showClear: this.allowBlank,
39235             minDate : this.minValue,
39236             maxDate : this.maxValue,
39237             disabledDatesRE : this.ddMatch,
39238             disabledDatesText : this.disabledDatesText,
39239             
39240             format : this.useIso ? 'Y-m-d' : this.format,
39241             minText : String.format(this.minText, this.formatDate(this.minValue)),
39242             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39243             
39244         });
39245          this.menu.on(Roo.apply({}, this.menuListeners, {
39246             scope:this
39247         }));
39248        
39249         
39250         var m = this.menu;
39251         var p = m.picker;
39252         
39253         // hide month picker get's called when we called by 'before hide';
39254         
39255         var ignorehide = true;
39256         p.hideMonthPicker  = function(disableAnim){
39257             if (ignorehide) {
39258                 return;
39259             }
39260              if(this.monthPicker){
39261                 Roo.log("hideMonthPicker called");
39262                 if(disableAnim === true){
39263                     this.monthPicker.hide();
39264                 }else{
39265                     this.monthPicker.slideOut('t', {duration:.2});
39266                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39267                     p.fireEvent("select", this, this.value);
39268                     m.hide();
39269                 }
39270             }
39271         }
39272         
39273         Roo.log('picker set value');
39274         Roo.log(this.getValue());
39275         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39276         m.show(this.el, 'tl-bl?');
39277         ignorehide  = false;
39278         // this will trigger hideMonthPicker..
39279         
39280         
39281         // hidden the day picker
39282         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39283         
39284         
39285         
39286       
39287         
39288         p.showMonthPicker.defer(100, p);
39289     
39290         
39291        
39292     },
39293
39294     beforeBlur : function(){
39295         var v = this.parseDate(this.getRawValue());
39296         if(v){
39297             this.setValue(v);
39298         }
39299     }
39300
39301     /** @cfg {Boolean} grow @hide */
39302     /** @cfg {Number} growMin @hide */
39303     /** @cfg {Number} growMax @hide */
39304     /**
39305      * @hide
39306      * @method autoSize
39307      */
39308 });/*
39309  * Based on:
39310  * Ext JS Library 1.1.1
39311  * Copyright(c) 2006-2007, Ext JS, LLC.
39312  *
39313  * Originally Released Under LGPL - original licence link has changed is not relivant.
39314  *
39315  * Fork - LGPL
39316  * <script type="text/javascript">
39317  */
39318  
39319
39320 /**
39321  * @class Roo.form.ComboBox
39322  * @extends Roo.form.TriggerField
39323  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39324  * @constructor
39325  * Create a new ComboBox.
39326  * @param {Object} config Configuration options
39327  */
39328 Roo.form.ComboBox = function(config){
39329     Roo.form.ComboBox.superclass.constructor.call(this, config);
39330     this.addEvents({
39331         /**
39332          * @event expand
39333          * Fires when the dropdown list is expanded
39334              * @param {Roo.form.ComboBox} combo This combo box
39335              */
39336         'expand' : true,
39337         /**
39338          * @event collapse
39339          * Fires when the dropdown list is collapsed
39340              * @param {Roo.form.ComboBox} combo This combo box
39341              */
39342         'collapse' : true,
39343         /**
39344          * @event beforeselect
39345          * Fires before a list item is selected. Return false to cancel the selection.
39346              * @param {Roo.form.ComboBox} combo This combo box
39347              * @param {Roo.data.Record} record The data record returned from the underlying store
39348              * @param {Number} index The index of the selected item in the dropdown list
39349              */
39350         'beforeselect' : true,
39351         /**
39352          * @event select
39353          * Fires when a list item is selected
39354              * @param {Roo.form.ComboBox} combo This combo box
39355              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39356              * @param {Number} index The index of the selected item in the dropdown list
39357              */
39358         'select' : true,
39359         /**
39360          * @event beforequery
39361          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39362          * The event object passed has these properties:
39363              * @param {Roo.form.ComboBox} combo This combo box
39364              * @param {String} query The query
39365              * @param {Boolean} forceAll true to force "all" query
39366              * @param {Boolean} cancel true to cancel the query
39367              * @param {Object} e The query event object
39368              */
39369         'beforequery': true,
39370          /**
39371          * @event add
39372          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39373              * @param {Roo.form.ComboBox} combo This combo box
39374              */
39375         'add' : true,
39376         /**
39377          * @event edit
39378          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39379              * @param {Roo.form.ComboBox} combo This combo box
39380              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39381              */
39382         'edit' : true
39383         
39384         
39385     });
39386     if(this.transform){
39387         this.allowDomMove = false;
39388         var s = Roo.getDom(this.transform);
39389         if(!this.hiddenName){
39390             this.hiddenName = s.name;
39391         }
39392         if(!this.store){
39393             this.mode = 'local';
39394             var d = [], opts = s.options;
39395             for(var i = 0, len = opts.length;i < len; i++){
39396                 var o = opts[i];
39397                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39398                 if(o.selected) {
39399                     this.value = value;
39400                 }
39401                 d.push([value, o.text]);
39402             }
39403             this.store = new Roo.data.SimpleStore({
39404                 'id': 0,
39405                 fields: ['value', 'text'],
39406                 data : d
39407             });
39408             this.valueField = 'value';
39409             this.displayField = 'text';
39410         }
39411         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39412         if(!this.lazyRender){
39413             this.target = true;
39414             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39415             s.parentNode.removeChild(s); // remove it
39416             this.render(this.el.parentNode);
39417         }else{
39418             s.parentNode.removeChild(s); // remove it
39419         }
39420
39421     }
39422     if (this.store) {
39423         this.store = Roo.factory(this.store, Roo.data);
39424     }
39425     
39426     this.selectedIndex = -1;
39427     if(this.mode == 'local'){
39428         if(config.queryDelay === undefined){
39429             this.queryDelay = 10;
39430         }
39431         if(config.minChars === undefined){
39432             this.minChars = 0;
39433         }
39434     }
39435 };
39436
39437 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39438     /**
39439      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39440      */
39441     /**
39442      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39443      * rendering into an Roo.Editor, defaults to false)
39444      */
39445     /**
39446      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39447      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39448      */
39449     /**
39450      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39451      */
39452     /**
39453      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39454      * the dropdown list (defaults to undefined, with no header element)
39455      */
39456
39457      /**
39458      * @cfg {String/Roo.Template} tpl The template to use to render the output
39459      */
39460      
39461     // private
39462     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39463     /**
39464      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39465      */
39466     listWidth: undefined,
39467     /**
39468      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39469      * mode = 'remote' or 'text' if mode = 'local')
39470      */
39471     displayField: undefined,
39472     /**
39473      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39474      * mode = 'remote' or 'value' if mode = 'local'). 
39475      * Note: use of a valueField requires the user make a selection
39476      * in order for a value to be mapped.
39477      */
39478     valueField: undefined,
39479     
39480     
39481     /**
39482      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39483      * field's data value (defaults to the underlying DOM element's name)
39484      */
39485     hiddenName: undefined,
39486     /**
39487      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39488      */
39489     listClass: '',
39490     /**
39491      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39492      */
39493     selectedClass: 'x-combo-selected',
39494     /**
39495      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39496      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39497      * which displays a downward arrow icon).
39498      */
39499     triggerClass : 'x-form-arrow-trigger',
39500     /**
39501      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39502      */
39503     shadow:'sides',
39504     /**
39505      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39506      * anchor positions (defaults to 'tl-bl')
39507      */
39508     listAlign: 'tl-bl?',
39509     /**
39510      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39511      */
39512     maxHeight: 300,
39513     /**
39514      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39515      * query specified by the allQuery config option (defaults to 'query')
39516      */
39517     triggerAction: 'query',
39518     /**
39519      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39520      * (defaults to 4, does not apply if editable = false)
39521      */
39522     minChars : 4,
39523     /**
39524      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39525      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39526      */
39527     typeAhead: false,
39528     /**
39529      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39530      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39531      */
39532     queryDelay: 500,
39533     /**
39534      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39535      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39536      */
39537     pageSize: 0,
39538     /**
39539      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39540      * when editable = true (defaults to false)
39541      */
39542     selectOnFocus:false,
39543     /**
39544      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39545      */
39546     queryParam: 'query',
39547     /**
39548      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39549      * when mode = 'remote' (defaults to 'Loading...')
39550      */
39551     loadingText: 'Loading...',
39552     /**
39553      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39554      */
39555     resizable: false,
39556     /**
39557      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39558      */
39559     handleHeight : 8,
39560     /**
39561      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39562      * traditional select (defaults to true)
39563      */
39564     editable: true,
39565     /**
39566      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39567      */
39568     allQuery: '',
39569     /**
39570      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39571      */
39572     mode: 'remote',
39573     /**
39574      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39575      * listWidth has a higher value)
39576      */
39577     minListWidth : 70,
39578     /**
39579      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39580      * allow the user to set arbitrary text into the field (defaults to false)
39581      */
39582     forceSelection:false,
39583     /**
39584      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39585      * if typeAhead = true (defaults to 250)
39586      */
39587     typeAheadDelay : 250,
39588     /**
39589      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39590      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39591      */
39592     valueNotFoundText : undefined,
39593     /**
39594      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39595      */
39596     blockFocus : false,
39597     
39598     /**
39599      * @cfg {Boolean} disableClear Disable showing of clear button.
39600      */
39601     disableClear : false,
39602     /**
39603      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39604      */
39605     alwaysQuery : false,
39606     
39607     //private
39608     addicon : false,
39609     editicon: false,
39610     
39611     // element that contains real text value.. (when hidden is used..)
39612      
39613     // private
39614     onRender : function(ct, position){
39615         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39616         if(this.hiddenName){
39617             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39618                     'before', true);
39619             this.hiddenField.value =
39620                 this.hiddenValue !== undefined ? this.hiddenValue :
39621                 this.value !== undefined ? this.value : '';
39622
39623             // prevent input submission
39624             this.el.dom.removeAttribute('name');
39625              
39626              
39627         }
39628         if(Roo.isGecko){
39629             this.el.dom.setAttribute('autocomplete', 'off');
39630         }
39631
39632         var cls = 'x-combo-list';
39633
39634         this.list = new Roo.Layer({
39635             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39636         });
39637
39638         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39639         this.list.setWidth(lw);
39640         this.list.swallowEvent('mousewheel');
39641         this.assetHeight = 0;
39642
39643         if(this.title){
39644             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39645             this.assetHeight += this.header.getHeight();
39646         }
39647
39648         this.innerList = this.list.createChild({cls:cls+'-inner'});
39649         this.innerList.on('mouseover', this.onViewOver, this);
39650         this.innerList.on('mousemove', this.onViewMove, this);
39651         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39652         
39653         if(this.allowBlank && !this.pageSize && !this.disableClear){
39654             this.footer = this.list.createChild({cls:cls+'-ft'});
39655             this.pageTb = new Roo.Toolbar(this.footer);
39656            
39657         }
39658         if(this.pageSize){
39659             this.footer = this.list.createChild({cls:cls+'-ft'});
39660             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39661                     {pageSize: this.pageSize});
39662             
39663         }
39664         
39665         if (this.pageTb && this.allowBlank && !this.disableClear) {
39666             var _this = this;
39667             this.pageTb.add(new Roo.Toolbar.Fill(), {
39668                 cls: 'x-btn-icon x-btn-clear',
39669                 text: '&#160;',
39670                 handler: function()
39671                 {
39672                     _this.collapse();
39673                     _this.clearValue();
39674                     _this.onSelect(false, -1);
39675                 }
39676             });
39677         }
39678         if (this.footer) {
39679             this.assetHeight += this.footer.getHeight();
39680         }
39681         
39682
39683         if(!this.tpl){
39684             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39685         }
39686
39687         this.view = new Roo.View(this.innerList, this.tpl, {
39688             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39689         });
39690
39691         this.view.on('click', this.onViewClick, this);
39692
39693         this.store.on('beforeload', this.onBeforeLoad, this);
39694         this.store.on('load', this.onLoad, this);
39695         this.store.on('loadexception', this.onLoadException, this);
39696
39697         if(this.resizable){
39698             this.resizer = new Roo.Resizable(this.list,  {
39699                pinned:true, handles:'se'
39700             });
39701             this.resizer.on('resize', function(r, w, h){
39702                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39703                 this.listWidth = w;
39704                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39705                 this.restrictHeight();
39706             }, this);
39707             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39708         }
39709         if(!this.editable){
39710             this.editable = true;
39711             this.setEditable(false);
39712         }  
39713         
39714         
39715         if (typeof(this.events.add.listeners) != 'undefined') {
39716             
39717             this.addicon = this.wrap.createChild(
39718                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39719        
39720             this.addicon.on('click', function(e) {
39721                 this.fireEvent('add', this);
39722             }, this);
39723         }
39724         if (typeof(this.events.edit.listeners) != 'undefined') {
39725             
39726             this.editicon = this.wrap.createChild(
39727                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39728             if (this.addicon) {
39729                 this.editicon.setStyle('margin-left', '40px');
39730             }
39731             this.editicon.on('click', function(e) {
39732                 
39733                 // we fire even  if inothing is selected..
39734                 this.fireEvent('edit', this, this.lastData );
39735                 
39736             }, this);
39737         }
39738         
39739         
39740         
39741     },
39742
39743     // private
39744     initEvents : function(){
39745         Roo.form.ComboBox.superclass.initEvents.call(this);
39746
39747         this.keyNav = new Roo.KeyNav(this.el, {
39748             "up" : function(e){
39749                 this.inKeyMode = true;
39750                 this.selectPrev();
39751             },
39752
39753             "down" : function(e){
39754                 if(!this.isExpanded()){
39755                     this.onTriggerClick();
39756                 }else{
39757                     this.inKeyMode = true;
39758                     this.selectNext();
39759                 }
39760             },
39761
39762             "enter" : function(e){
39763                 this.onViewClick();
39764                 //return true;
39765             },
39766
39767             "esc" : function(e){
39768                 this.collapse();
39769             },
39770
39771             "tab" : function(e){
39772                 this.onViewClick(false);
39773                 this.fireEvent("specialkey", this, e);
39774                 return true;
39775             },
39776
39777             scope : this,
39778
39779             doRelay : function(foo, bar, hname){
39780                 if(hname == 'down' || this.scope.isExpanded()){
39781                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39782                 }
39783                 return true;
39784             },
39785
39786             forceKeyDown: true
39787         });
39788         this.queryDelay = Math.max(this.queryDelay || 10,
39789                 this.mode == 'local' ? 10 : 250);
39790         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39791         if(this.typeAhead){
39792             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39793         }
39794         if(this.editable !== false){
39795             this.el.on("keyup", this.onKeyUp, this);
39796         }
39797         if(this.forceSelection){
39798             this.on('blur', this.doForce, this);
39799         }
39800     },
39801
39802     onDestroy : function(){
39803         if(this.view){
39804             this.view.setStore(null);
39805             this.view.el.removeAllListeners();
39806             this.view.el.remove();
39807             this.view.purgeListeners();
39808         }
39809         if(this.list){
39810             this.list.destroy();
39811         }
39812         if(this.store){
39813             this.store.un('beforeload', this.onBeforeLoad, this);
39814             this.store.un('load', this.onLoad, this);
39815             this.store.un('loadexception', this.onLoadException, this);
39816         }
39817         Roo.form.ComboBox.superclass.onDestroy.call(this);
39818     },
39819
39820     // private
39821     fireKey : function(e){
39822         if(e.isNavKeyPress() && !this.list.isVisible()){
39823             this.fireEvent("specialkey", this, e);
39824         }
39825     },
39826
39827     // private
39828     onResize: function(w, h){
39829         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39830         
39831         if(typeof w != 'number'){
39832             // we do not handle it!?!?
39833             return;
39834         }
39835         var tw = this.trigger.getWidth();
39836         tw += this.addicon ? this.addicon.getWidth() : 0;
39837         tw += this.editicon ? this.editicon.getWidth() : 0;
39838         var x = w - tw;
39839         this.el.setWidth( this.adjustWidth('input', x));
39840             
39841         this.trigger.setStyle('left', x+'px');
39842         
39843         if(this.list && this.listWidth === undefined){
39844             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39845             this.list.setWidth(lw);
39846             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39847         }
39848         
39849     
39850         
39851     },
39852
39853     /**
39854      * Allow or prevent the user from directly editing the field text.  If false is passed,
39855      * the user will only be able to select from the items defined in the dropdown list.  This method
39856      * is the runtime equivalent of setting the 'editable' config option at config time.
39857      * @param {Boolean} value True to allow the user to directly edit the field text
39858      */
39859     setEditable : function(value){
39860         if(value == this.editable){
39861             return;
39862         }
39863         this.editable = value;
39864         if(!value){
39865             this.el.dom.setAttribute('readOnly', true);
39866             this.el.on('mousedown', this.onTriggerClick,  this);
39867             this.el.addClass('x-combo-noedit');
39868         }else{
39869             this.el.dom.setAttribute('readOnly', false);
39870             this.el.un('mousedown', this.onTriggerClick,  this);
39871             this.el.removeClass('x-combo-noedit');
39872         }
39873     },
39874
39875     // private
39876     onBeforeLoad : function(){
39877         if(!this.hasFocus){
39878             return;
39879         }
39880         this.innerList.update(this.loadingText ?
39881                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39882         this.restrictHeight();
39883         this.selectedIndex = -1;
39884     },
39885
39886     // private
39887     onLoad : function(){
39888         if(!this.hasFocus){
39889             return;
39890         }
39891         if(this.store.getCount() > 0){
39892             this.expand();
39893             this.restrictHeight();
39894             if(this.lastQuery == this.allQuery){
39895                 if(this.editable){
39896                     this.el.dom.select();
39897                 }
39898                 if(!this.selectByValue(this.value, true)){
39899                     this.select(0, true);
39900                 }
39901             }else{
39902                 this.selectNext();
39903                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39904                     this.taTask.delay(this.typeAheadDelay);
39905                 }
39906             }
39907         }else{
39908             this.onEmptyResults();
39909         }
39910         //this.el.focus();
39911     },
39912     // private
39913     onLoadException : function()
39914     {
39915         this.collapse();
39916         Roo.log(this.store.reader.jsonData);
39917         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39918             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39919         }
39920         
39921         
39922     },
39923     // private
39924     onTypeAhead : function(){
39925         if(this.store.getCount() > 0){
39926             var r = this.store.getAt(0);
39927             var newValue = r.data[this.displayField];
39928             var len = newValue.length;
39929             var selStart = this.getRawValue().length;
39930             if(selStart != len){
39931                 this.setRawValue(newValue);
39932                 this.selectText(selStart, newValue.length);
39933             }
39934         }
39935     },
39936
39937     // private
39938     onSelect : function(record, index){
39939         if(this.fireEvent('beforeselect', this, record, index) !== false){
39940             this.setFromData(index > -1 ? record.data : false);
39941             this.collapse();
39942             this.fireEvent('select', this, record, index);
39943         }
39944     },
39945
39946     /**
39947      * Returns the currently selected field value or empty string if no value is set.
39948      * @return {String} value The selected value
39949      */
39950     getValue : function(){
39951         if(this.valueField){
39952             return typeof this.value != 'undefined' ? this.value : '';
39953         }
39954         return Roo.form.ComboBox.superclass.getValue.call(this);
39955     },
39956
39957     /**
39958      * Clears any text/value currently set in the field
39959      */
39960     clearValue : function(){
39961         if(this.hiddenField){
39962             this.hiddenField.value = '';
39963         }
39964         this.value = '';
39965         this.setRawValue('');
39966         this.lastSelectionText = '';
39967         
39968     },
39969
39970     /**
39971      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39972      * will be displayed in the field.  If the value does not match the data value of an existing item,
39973      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39974      * Otherwise the field will be blank (although the value will still be set).
39975      * @param {String} value The value to match
39976      */
39977     setValue : function(v){
39978         var text = v;
39979         if(this.valueField){
39980             var r = this.findRecord(this.valueField, v);
39981             if(r){
39982                 text = r.data[this.displayField];
39983             }else if(this.valueNotFoundText !== undefined){
39984                 text = this.valueNotFoundText;
39985             }
39986         }
39987         this.lastSelectionText = text;
39988         if(this.hiddenField){
39989             this.hiddenField.value = v;
39990         }
39991         Roo.form.ComboBox.superclass.setValue.call(this, text);
39992         this.value = v;
39993     },
39994     /**
39995      * @property {Object} the last set data for the element
39996      */
39997     
39998     lastData : false,
39999     /**
40000      * Sets the value of the field based on a object which is related to the record format for the store.
40001      * @param {Object} value the value to set as. or false on reset?
40002      */
40003     setFromData : function(o){
40004         var dv = ''; // display value
40005         var vv = ''; // value value..
40006         this.lastData = o;
40007         if (this.displayField) {
40008             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
40009         } else {
40010             // this is an error condition!!!
40011             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
40012         }
40013         
40014         if(this.valueField){
40015             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
40016         }
40017         if(this.hiddenField){
40018             this.hiddenField.value = vv;
40019             
40020             this.lastSelectionText = dv;
40021             Roo.form.ComboBox.superclass.setValue.call(this, dv);
40022             this.value = vv;
40023             return;
40024         }
40025         // no hidden field.. - we store the value in 'value', but still display
40026         // display field!!!!
40027         this.lastSelectionText = dv;
40028         Roo.form.ComboBox.superclass.setValue.call(this, dv);
40029         this.value = vv;
40030         
40031         
40032     },
40033     // private
40034     reset : function(){
40035         // overridden so that last data is reset..
40036         this.setValue(this.resetValue);
40037         this.clearInvalid();
40038         this.lastData = false;
40039         if (this.view) {
40040             this.view.clearSelections();
40041         }
40042     },
40043     // private
40044     findRecord : function(prop, value){
40045         var record;
40046         if(this.store.getCount() > 0){
40047             this.store.each(function(r){
40048                 if(r.data[prop] == value){
40049                     record = r;
40050                     return false;
40051                 }
40052                 return true;
40053             });
40054         }
40055         return record;
40056     },
40057     
40058     getName: function()
40059     {
40060         // returns hidden if it's set..
40061         if (!this.rendered) {return ''};
40062         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40063         
40064     },
40065     // private
40066     onViewMove : function(e, t){
40067         this.inKeyMode = false;
40068     },
40069
40070     // private
40071     onViewOver : function(e, t){
40072         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40073             return;
40074         }
40075         var item = this.view.findItemFromChild(t);
40076         if(item){
40077             var index = this.view.indexOf(item);
40078             this.select(index, false);
40079         }
40080     },
40081
40082     // private
40083     onViewClick : function(doFocus)
40084     {
40085         var index = this.view.getSelectedIndexes()[0];
40086         var r = this.store.getAt(index);
40087         if(r){
40088             this.onSelect(r, index);
40089         }
40090         if(doFocus !== false && !this.blockFocus){
40091             this.el.focus();
40092         }
40093     },
40094
40095     // private
40096     restrictHeight : function(){
40097         this.innerList.dom.style.height = '';
40098         var inner = this.innerList.dom;
40099         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40100         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40101         this.list.beginUpdate();
40102         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40103         this.list.alignTo(this.el, this.listAlign);
40104         this.list.endUpdate();
40105     },
40106
40107     // private
40108     onEmptyResults : function(){
40109         this.collapse();
40110     },
40111
40112     /**
40113      * Returns true if the dropdown list is expanded, else false.
40114      */
40115     isExpanded : function(){
40116         return this.list.isVisible();
40117     },
40118
40119     /**
40120      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40121      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40122      * @param {String} value The data value of the item to select
40123      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40124      * selected item if it is not currently in view (defaults to true)
40125      * @return {Boolean} True if the value matched an item in the list, else false
40126      */
40127     selectByValue : function(v, scrollIntoView){
40128         if(v !== undefined && v !== null){
40129             var r = this.findRecord(this.valueField || this.displayField, v);
40130             if(r){
40131                 this.select(this.store.indexOf(r), scrollIntoView);
40132                 return true;
40133             }
40134         }
40135         return false;
40136     },
40137
40138     /**
40139      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40140      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40141      * @param {Number} index The zero-based index of the list item to select
40142      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40143      * selected item if it is not currently in view (defaults to true)
40144      */
40145     select : function(index, scrollIntoView){
40146         this.selectedIndex = index;
40147         this.view.select(index);
40148         if(scrollIntoView !== false){
40149             var el = this.view.getNode(index);
40150             if(el){
40151                 this.innerList.scrollChildIntoView(el, false);
40152             }
40153         }
40154     },
40155
40156     // private
40157     selectNext : function(){
40158         var ct = this.store.getCount();
40159         if(ct > 0){
40160             if(this.selectedIndex == -1){
40161                 this.select(0);
40162             }else if(this.selectedIndex < ct-1){
40163                 this.select(this.selectedIndex+1);
40164             }
40165         }
40166     },
40167
40168     // private
40169     selectPrev : function(){
40170         var ct = this.store.getCount();
40171         if(ct > 0){
40172             if(this.selectedIndex == -1){
40173                 this.select(0);
40174             }else if(this.selectedIndex != 0){
40175                 this.select(this.selectedIndex-1);
40176             }
40177         }
40178     },
40179
40180     // private
40181     onKeyUp : function(e){
40182         if(this.editable !== false && !e.isSpecialKey()){
40183             this.lastKey = e.getKey();
40184             this.dqTask.delay(this.queryDelay);
40185         }
40186     },
40187
40188     // private
40189     validateBlur : function(){
40190         return !this.list || !this.list.isVisible();   
40191     },
40192
40193     // private
40194     initQuery : function(){
40195         this.doQuery(this.getRawValue());
40196     },
40197
40198     // private
40199     doForce : function(){
40200         if(this.el.dom.value.length > 0){
40201             this.el.dom.value =
40202                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40203              
40204         }
40205     },
40206
40207     /**
40208      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40209      * query allowing the query action to be canceled if needed.
40210      * @param {String} query The SQL query to execute
40211      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40212      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40213      * saved in the current store (defaults to false)
40214      */
40215     doQuery : function(q, forceAll){
40216         if(q === undefined || q === null){
40217             q = '';
40218         }
40219         var qe = {
40220             query: q,
40221             forceAll: forceAll,
40222             combo: this,
40223             cancel:false
40224         };
40225         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40226             return false;
40227         }
40228         q = qe.query;
40229         forceAll = qe.forceAll;
40230         if(forceAll === true || (q.length >= this.minChars)){
40231             if(this.lastQuery != q || this.alwaysQuery){
40232                 this.lastQuery = q;
40233                 if(this.mode == 'local'){
40234                     this.selectedIndex = -1;
40235                     if(forceAll){
40236                         this.store.clearFilter();
40237                     }else{
40238                         this.store.filter(this.displayField, q);
40239                     }
40240                     this.onLoad();
40241                 }else{
40242                     this.store.baseParams[this.queryParam] = q;
40243                     this.store.load({
40244                         params: this.getParams(q)
40245                     });
40246                     this.expand();
40247                 }
40248             }else{
40249                 this.selectedIndex = -1;
40250                 this.onLoad();   
40251             }
40252         }
40253     },
40254
40255     // private
40256     getParams : function(q){
40257         var p = {};
40258         //p[this.queryParam] = q;
40259         if(this.pageSize){
40260             p.start = 0;
40261             p.limit = this.pageSize;
40262         }
40263         return p;
40264     },
40265
40266     /**
40267      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40268      */
40269     collapse : function(){
40270         if(!this.isExpanded()){
40271             return;
40272         }
40273         this.list.hide();
40274         Roo.get(document).un('mousedown', this.collapseIf, this);
40275         Roo.get(document).un('mousewheel', this.collapseIf, this);
40276         if (!this.editable) {
40277             Roo.get(document).un('keydown', this.listKeyPress, this);
40278         }
40279         this.fireEvent('collapse', this);
40280     },
40281
40282     // private
40283     collapseIf : function(e){
40284         if(!e.within(this.wrap) && !e.within(this.list)){
40285             this.collapse();
40286         }
40287     },
40288
40289     /**
40290      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40291      */
40292     expand : function(){
40293         if(this.isExpanded() || !this.hasFocus){
40294             return;
40295         }
40296         this.list.alignTo(this.el, this.listAlign);
40297         this.list.show();
40298         Roo.get(document).on('mousedown', this.collapseIf, this);
40299         Roo.get(document).on('mousewheel', this.collapseIf, this);
40300         if (!this.editable) {
40301             Roo.get(document).on('keydown', this.listKeyPress, this);
40302         }
40303         
40304         this.fireEvent('expand', this);
40305     },
40306
40307     // private
40308     // Implements the default empty TriggerField.onTriggerClick function
40309     onTriggerClick : function(){
40310         if(this.disabled){
40311             return;
40312         }
40313         if(this.isExpanded()){
40314             this.collapse();
40315             if (!this.blockFocus) {
40316                 this.el.focus();
40317             }
40318             
40319         }else {
40320             this.hasFocus = true;
40321             if(this.triggerAction == 'all') {
40322                 this.doQuery(this.allQuery, true);
40323             } else {
40324                 this.doQuery(this.getRawValue());
40325             }
40326             if (!this.blockFocus) {
40327                 this.el.focus();
40328             }
40329         }
40330     },
40331     listKeyPress : function(e)
40332     {
40333         //Roo.log('listkeypress');
40334         // scroll to first matching element based on key pres..
40335         if (e.isSpecialKey()) {
40336             return false;
40337         }
40338         var k = String.fromCharCode(e.getKey()).toUpperCase();
40339         //Roo.log(k);
40340         var match  = false;
40341         var csel = this.view.getSelectedNodes();
40342         var cselitem = false;
40343         if (csel.length) {
40344             var ix = this.view.indexOf(csel[0]);
40345             cselitem  = this.store.getAt(ix);
40346             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40347                 cselitem = false;
40348             }
40349             
40350         }
40351         
40352         this.store.each(function(v) { 
40353             if (cselitem) {
40354                 // start at existing selection.
40355                 if (cselitem.id == v.id) {
40356                     cselitem = false;
40357                 }
40358                 return;
40359             }
40360                 
40361             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40362                 match = this.store.indexOf(v);
40363                 return false;
40364             }
40365         }, this);
40366         
40367         if (match === false) {
40368             return true; // no more action?
40369         }
40370         // scroll to?
40371         this.view.select(match);
40372         var sn = Roo.get(this.view.getSelectedNodes()[0]);
40373         sn.scrollIntoView(sn.dom.parentNode, false);
40374     }
40375
40376     /** 
40377     * @cfg {Boolean} grow 
40378     * @hide 
40379     */
40380     /** 
40381     * @cfg {Number} growMin 
40382     * @hide 
40383     */
40384     /** 
40385     * @cfg {Number} growMax 
40386     * @hide 
40387     */
40388     /**
40389      * @hide
40390      * @method autoSize
40391      */
40392 });/*
40393  * Copyright(c) 2010-2012, Roo J Solutions Limited
40394  *
40395  * Licence LGPL
40396  *
40397  */
40398
40399 /**
40400  * @class Roo.form.ComboBoxArray
40401  * @extends Roo.form.TextField
40402  * A facebook style adder... for lists of email / people / countries  etc...
40403  * pick multiple items from a combo box, and shows each one.
40404  *
40405  *  Fred [x]  Brian [x]  [Pick another |v]
40406  *
40407  *
40408  *  For this to work: it needs various extra information
40409  *    - normal combo problay has
40410  *      name, hiddenName
40411  *    + displayField, valueField
40412  *
40413  *    For our purpose...
40414  *
40415  *
40416  *   If we change from 'extends' to wrapping...
40417  *   
40418  *  
40419  *
40420  
40421  
40422  * @constructor
40423  * Create a new ComboBoxArray.
40424  * @param {Object} config Configuration options
40425  */
40426  
40427
40428 Roo.form.ComboBoxArray = function(config)
40429 {
40430     this.addEvents({
40431         /**
40432          * @event beforeremove
40433          * Fires before remove the value from the list
40434              * @param {Roo.form.ComboBoxArray} _self This combo box array
40435              * @param {Roo.form.ComboBoxArray.Item} item removed item
40436              */
40437         'beforeremove' : true,
40438         /**
40439          * @event remove
40440          * Fires when remove the value from the list
40441              * @param {Roo.form.ComboBoxArray} _self This combo box array
40442              * @param {Roo.form.ComboBoxArray.Item} item removed item
40443              */
40444         'remove' : true
40445         
40446         
40447     });
40448     
40449     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40450     
40451     this.items = new Roo.util.MixedCollection(false);
40452     
40453     // construct the child combo...
40454     
40455     
40456     
40457     
40458    
40459     
40460 }
40461
40462  
40463 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40464
40465     /**
40466      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40467      */
40468     
40469     lastData : false,
40470     
40471     // behavies liek a hiddne field
40472     inputType:      'hidden',
40473     /**
40474      * @cfg {Number} width The width of the box that displays the selected element
40475      */ 
40476     width:          300,
40477
40478     
40479     
40480     /**
40481      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40482      */
40483     name : false,
40484     /**
40485      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40486      */
40487     hiddenName : false,
40488     
40489     
40490     // private the array of items that are displayed..
40491     items  : false,
40492     // private - the hidden field el.
40493     hiddenEl : false,
40494     // private - the filed el..
40495     el : false,
40496     
40497     //validateValue : function() { return true; }, // all values are ok!
40498     //onAddClick: function() { },
40499     
40500     onRender : function(ct, position) 
40501     {
40502         
40503         // create the standard hidden element
40504         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40505         
40506         
40507         // give fake names to child combo;
40508         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40509         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40510         
40511         this.combo = Roo.factory(this.combo, Roo.form);
40512         this.combo.onRender(ct, position);
40513         if (typeof(this.combo.width) != 'undefined') {
40514             this.combo.onResize(this.combo.width,0);
40515         }
40516         
40517         this.combo.initEvents();
40518         
40519         // assigned so form know we need to do this..
40520         this.store          = this.combo.store;
40521         this.valueField     = this.combo.valueField;
40522         this.displayField   = this.combo.displayField ;
40523         
40524         
40525         this.combo.wrap.addClass('x-cbarray-grp');
40526         
40527         var cbwrap = this.combo.wrap.createChild(
40528             {tag: 'div', cls: 'x-cbarray-cb'},
40529             this.combo.el.dom
40530         );
40531         
40532              
40533         this.hiddenEl = this.combo.wrap.createChild({
40534             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40535         });
40536         this.el = this.combo.wrap.createChild({
40537             tag: 'input',  type:'hidden' , name: this.name, value : ''
40538         });
40539          //   this.el.dom.removeAttribute("name");
40540         
40541         
40542         this.outerWrap = this.combo.wrap;
40543         this.wrap = cbwrap;
40544         
40545         this.outerWrap.setWidth(this.width);
40546         this.outerWrap.dom.removeChild(this.el.dom);
40547         
40548         this.wrap.dom.appendChild(this.el.dom);
40549         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40550         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40551         
40552         this.combo.trigger.setStyle('position','relative');
40553         this.combo.trigger.setStyle('left', '0px');
40554         this.combo.trigger.setStyle('top', '2px');
40555         
40556         this.combo.el.setStyle('vertical-align', 'text-bottom');
40557         
40558         //this.trigger.setStyle('vertical-align', 'top');
40559         
40560         // this should use the code from combo really... on('add' ....)
40561         if (this.adder) {
40562             
40563         
40564             this.adder = this.outerWrap.createChild(
40565                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40566             var _t = this;
40567             this.adder.on('click', function(e) {
40568                 _t.fireEvent('adderclick', this, e);
40569             }, _t);
40570         }
40571         //var _t = this;
40572         //this.adder.on('click', this.onAddClick, _t);
40573         
40574         
40575         this.combo.on('select', function(cb, rec, ix) {
40576             this.addItem(rec.data);
40577             
40578             cb.setValue('');
40579             cb.el.dom.value = '';
40580             //cb.lastData = rec.data;
40581             // add to list
40582             
40583         }, this);
40584         
40585         
40586     },
40587     
40588     
40589     getName: function()
40590     {
40591         // returns hidden if it's set..
40592         if (!this.rendered) {return ''};
40593         return  this.hiddenName ? this.hiddenName : this.name;
40594         
40595     },
40596     
40597     
40598     onResize: function(w, h){
40599         
40600         return;
40601         // not sure if this is needed..
40602         //this.combo.onResize(w,h);
40603         
40604         if(typeof w != 'number'){
40605             // we do not handle it!?!?
40606             return;
40607         }
40608         var tw = this.combo.trigger.getWidth();
40609         tw += this.addicon ? this.addicon.getWidth() : 0;
40610         tw += this.editicon ? this.editicon.getWidth() : 0;
40611         var x = w - tw;
40612         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40613             
40614         this.combo.trigger.setStyle('left', '0px');
40615         
40616         if(this.list && this.listWidth === undefined){
40617             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40618             this.list.setWidth(lw);
40619             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40620         }
40621         
40622     
40623         
40624     },
40625     
40626     addItem: function(rec)
40627     {
40628         var valueField = this.combo.valueField;
40629         var displayField = this.combo.displayField;
40630         if (this.items.indexOfKey(rec[valueField]) > -1) {
40631             //console.log("GOT " + rec.data.id);
40632             return;
40633         }
40634         
40635         var x = new Roo.form.ComboBoxArray.Item({
40636             //id : rec[this.idField],
40637             data : rec,
40638             displayField : displayField ,
40639             tipField : displayField ,
40640             cb : this
40641         });
40642         // use the 
40643         this.items.add(rec[valueField],x);
40644         // add it before the element..
40645         this.updateHiddenEl();
40646         x.render(this.outerWrap, this.wrap.dom);
40647         // add the image handler..
40648     },
40649     
40650     updateHiddenEl : function()
40651     {
40652         this.validate();
40653         if (!this.hiddenEl) {
40654             return;
40655         }
40656         var ar = [];
40657         var idField = this.combo.valueField;
40658         
40659         this.items.each(function(f) {
40660             ar.push(f.data[idField]);
40661            
40662         });
40663         this.hiddenEl.dom.value = ar.join(',');
40664         this.validate();
40665     },
40666     
40667     reset : function()
40668     {
40669         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40670         this.items.each(function(f) {
40671            f.remove(); 
40672         });
40673         this.el.dom.value = '';
40674         if (this.hiddenEl) {
40675             this.hiddenEl.dom.value = '';
40676         }
40677         
40678     },
40679     getValue: function()
40680     {
40681         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40682     },
40683     setValue: function(v) // not a valid action - must use addItems..
40684     {
40685          
40686         this.reset();
40687         
40688         
40689         
40690         if (this.store.isLocal && (typeof(v) == 'string')) {
40691             // then we can use the store to find the values..
40692             // comma seperated at present.. this needs to allow JSON based encoding..
40693             this.hiddenEl.value  = v;
40694             var v_ar = [];
40695             Roo.each(v.split(','), function(k) {
40696                 Roo.log("CHECK " + this.valueField + ',' + k);
40697                 var li = this.store.query(this.valueField, k);
40698                 if (!li.length) {
40699                     return;
40700                 }
40701                 var add = {};
40702                 add[this.valueField] = k;
40703                 add[this.displayField] = li.item(0).data[this.displayField];
40704                 
40705                 this.addItem(add);
40706             }, this) 
40707              
40708         }
40709         if (typeof(v) == 'object' ) {
40710             // then let's assume it's an array of objects..
40711             Roo.each(v, function(l) {
40712                 this.addItem(l);
40713             }, this);
40714              
40715         }
40716         
40717         
40718     },
40719     setFromData: function(v)
40720     {
40721         // this recieves an object, if setValues is called.
40722         this.reset();
40723         this.el.dom.value = v[this.displayField];
40724         this.hiddenEl.dom.value = v[this.valueField];
40725         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40726             return;
40727         }
40728         var kv = v[this.valueField];
40729         var dv = v[this.displayField];
40730         kv = typeof(kv) != 'string' ? '' : kv;
40731         dv = typeof(dv) != 'string' ? '' : dv;
40732         
40733         
40734         var keys = kv.split(',');
40735         var display = dv.split(',');
40736         for (var i = 0 ; i < keys.length; i++) {
40737             
40738             add = {};
40739             add[this.valueField] = keys[i];
40740             add[this.displayField] = display[i];
40741             this.addItem(add);
40742         }
40743       
40744         
40745     },
40746     
40747     /**
40748      * Validates the combox array value
40749      * @return {Boolean} True if the value is valid, else false
40750      */
40751     validate : function(){
40752         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40753             this.clearInvalid();
40754             return true;
40755         }
40756         return false;
40757     },
40758     
40759     validateValue : function(value){
40760         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40761         
40762     },
40763     
40764     /*@
40765      * overide
40766      * 
40767      */
40768     isDirty : function() {
40769         if(this.disabled) {
40770             return false;
40771         }
40772         
40773         try {
40774             var d = Roo.decode(String(this.originalValue));
40775         } catch (e) {
40776             return String(this.getValue()) !== String(this.originalValue);
40777         }
40778         
40779         var originalValue = [];
40780         
40781         for (var i = 0; i < d.length; i++){
40782             originalValue.push(d[i][this.valueField]);
40783         }
40784         
40785         return String(this.getValue()) !== String(originalValue.join(','));
40786         
40787     }
40788     
40789 });
40790
40791
40792
40793 /**
40794  * @class Roo.form.ComboBoxArray.Item
40795  * @extends Roo.BoxComponent
40796  * A selected item in the list
40797  *  Fred [x]  Brian [x]  [Pick another |v]
40798  * 
40799  * @constructor
40800  * Create a new item.
40801  * @param {Object} config Configuration options
40802  */
40803  
40804 Roo.form.ComboBoxArray.Item = function(config) {
40805     config.id = Roo.id();
40806     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40807 }
40808
40809 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40810     data : {},
40811     cb: false,
40812     displayField : false,
40813     tipField : false,
40814     
40815     
40816     defaultAutoCreate : {
40817         tag: 'div',
40818         cls: 'x-cbarray-item',
40819         cn : [ 
40820             { tag: 'div' },
40821             {
40822                 tag: 'img',
40823                 width:16,
40824                 height : 16,
40825                 src : Roo.BLANK_IMAGE_URL ,
40826                 align: 'center'
40827             }
40828         ]
40829         
40830     },
40831     
40832  
40833     onRender : function(ct, position)
40834     {
40835         Roo.form.Field.superclass.onRender.call(this, ct, position);
40836         
40837         if(!this.el){
40838             var cfg = this.getAutoCreate();
40839             this.el = ct.createChild(cfg, position);
40840         }
40841         
40842         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40843         
40844         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40845             this.cb.renderer(this.data) :
40846             String.format('{0}',this.data[this.displayField]);
40847         
40848             
40849         this.el.child('div').dom.setAttribute('qtip',
40850                         String.format('{0}',this.data[this.tipField])
40851         );
40852         
40853         this.el.child('img').on('click', this.remove, this);
40854         
40855     },
40856    
40857     remove : function()
40858     {
40859         if(this.cb.disabled){
40860             return;
40861         }
40862         
40863         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40864             this.cb.items.remove(this);
40865             this.el.child('img').un('click', this.remove, this);
40866             this.el.remove();
40867             this.cb.updateHiddenEl();
40868
40869             this.cb.fireEvent('remove', this.cb, this);
40870         }
40871         
40872     }
40873 });/*
40874  * Based on:
40875  * Ext JS Library 1.1.1
40876  * Copyright(c) 2006-2007, Ext JS, LLC.
40877  *
40878  * Originally Released Under LGPL - original licence link has changed is not relivant.
40879  *
40880  * Fork - LGPL
40881  * <script type="text/javascript">
40882  */
40883 /**
40884  * @class Roo.form.Checkbox
40885  * @extends Roo.form.Field
40886  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40887  * @constructor
40888  * Creates a new Checkbox
40889  * @param {Object} config Configuration options
40890  */
40891 Roo.form.Checkbox = function(config){
40892     Roo.form.Checkbox.superclass.constructor.call(this, config);
40893     this.addEvents({
40894         /**
40895          * @event check
40896          * Fires when the checkbox is checked or unchecked.
40897              * @param {Roo.form.Checkbox} this This checkbox
40898              * @param {Boolean} checked The new checked value
40899              */
40900         check : true
40901     });
40902 };
40903
40904 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40905     /**
40906      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40907      */
40908     focusClass : undefined,
40909     /**
40910      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40911      */
40912     fieldClass: "x-form-field",
40913     /**
40914      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40915      */
40916     checked: false,
40917     /**
40918      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40919      * {tag: "input", type: "checkbox", autocomplete: "off"})
40920      */
40921     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40922     /**
40923      * @cfg {String} boxLabel The text that appears beside the checkbox
40924      */
40925     boxLabel : "",
40926     /**
40927      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40928      */  
40929     inputValue : '1',
40930     /**
40931      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40932      */
40933      valueOff: '0', // value when not checked..
40934
40935     actionMode : 'viewEl', 
40936     //
40937     // private
40938     itemCls : 'x-menu-check-item x-form-item',
40939     groupClass : 'x-menu-group-item',
40940     inputType : 'hidden',
40941     
40942     
40943     inSetChecked: false, // check that we are not calling self...
40944     
40945     inputElement: false, // real input element?
40946     basedOn: false, // ????
40947     
40948     isFormField: true, // not sure where this is needed!!!!
40949
40950     onResize : function(){
40951         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40952         if(!this.boxLabel){
40953             this.el.alignTo(this.wrap, 'c-c');
40954         }
40955     },
40956
40957     initEvents : function(){
40958         Roo.form.Checkbox.superclass.initEvents.call(this);
40959         this.el.on("click", this.onClick,  this);
40960         this.el.on("change", this.onClick,  this);
40961     },
40962
40963
40964     getResizeEl : function(){
40965         return this.wrap;
40966     },
40967
40968     getPositionEl : function(){
40969         return this.wrap;
40970     },
40971
40972     // private
40973     onRender : function(ct, position){
40974         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40975         /*
40976         if(this.inputValue !== undefined){
40977             this.el.dom.value = this.inputValue;
40978         }
40979         */
40980         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40981         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40982         var viewEl = this.wrap.createChild({ 
40983             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40984         this.viewEl = viewEl;   
40985         this.wrap.on('click', this.onClick,  this); 
40986         
40987         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40988         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40989         
40990         
40991         
40992         if(this.boxLabel){
40993             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40994         //    viewEl.on('click', this.onClick,  this); 
40995         }
40996         //if(this.checked){
40997             this.setChecked(this.checked);
40998         //}else{
40999             //this.checked = this.el.dom;
41000         //}
41001
41002     },
41003
41004     // private
41005     initValue : Roo.emptyFn,
41006
41007     /**
41008      * Returns the checked state of the checkbox.
41009      * @return {Boolean} True if checked, else false
41010      */
41011     getValue : function(){
41012         if(this.el){
41013             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
41014         }
41015         return this.valueOff;
41016         
41017     },
41018
41019         // private
41020     onClick : function(){ 
41021         if (this.disabled) {
41022             return;
41023         }
41024         this.setChecked(!this.checked);
41025
41026         //if(this.el.dom.checked != this.checked){
41027         //    this.setValue(this.el.dom.checked);
41028        // }
41029     },
41030
41031     /**
41032      * Sets the checked state of the checkbox.
41033      * On is always based on a string comparison between inputValue and the param.
41034      * @param {Boolean/String} value - the value to set 
41035      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
41036      */
41037     setValue : function(v,suppressEvent){
41038         
41039         
41040         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
41041         //if(this.el && this.el.dom){
41042         //    this.el.dom.checked = this.checked;
41043         //    this.el.dom.defaultChecked = this.checked;
41044         //}
41045         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
41046         //this.fireEvent("check", this, this.checked);
41047     },
41048     // private..
41049     setChecked : function(state,suppressEvent)
41050     {
41051         if (this.inSetChecked) {
41052             this.checked = state;
41053             return;
41054         }
41055         
41056     
41057         if(this.wrap){
41058             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41059         }
41060         this.checked = state;
41061         if(suppressEvent !== true){
41062             this.fireEvent('check', this, state);
41063         }
41064         this.inSetChecked = true;
41065         this.el.dom.value = state ? this.inputValue : this.valueOff;
41066         this.inSetChecked = false;
41067         
41068     },
41069     // handle setting of hidden value by some other method!!?!?
41070     setFromHidden: function()
41071     {
41072         if(!this.el){
41073             return;
41074         }
41075         //console.log("SET FROM HIDDEN");
41076         //alert('setFrom hidden');
41077         this.setValue(this.el.dom.value);
41078     },
41079     
41080     onDestroy : function()
41081     {
41082         if(this.viewEl){
41083             Roo.get(this.viewEl).remove();
41084         }
41085          
41086         Roo.form.Checkbox.superclass.onDestroy.call(this);
41087     }
41088
41089 });/*
41090  * Based on:
41091  * Ext JS Library 1.1.1
41092  * Copyright(c) 2006-2007, Ext JS, LLC.
41093  *
41094  * Originally Released Under LGPL - original licence link has changed is not relivant.
41095  *
41096  * Fork - LGPL
41097  * <script type="text/javascript">
41098  */
41099  
41100 /**
41101  * @class Roo.form.Radio
41102  * @extends Roo.form.Checkbox
41103  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41104  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41105  * @constructor
41106  * Creates a new Radio
41107  * @param {Object} config Configuration options
41108  */
41109 Roo.form.Radio = function(){
41110     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41111 };
41112 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41113     inputType: 'radio',
41114
41115     /**
41116      * If this radio is part of a group, it will return the selected value
41117      * @return {String}
41118      */
41119     getGroupValue : function(){
41120         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41121     },
41122     
41123     
41124     onRender : function(ct, position){
41125         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41126         
41127         if(this.inputValue !== undefined){
41128             this.el.dom.value = this.inputValue;
41129         }
41130          
41131         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41132         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41133         //var viewEl = this.wrap.createChild({ 
41134         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41135         //this.viewEl = viewEl;   
41136         //this.wrap.on('click', this.onClick,  this); 
41137         
41138         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41139         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41140         
41141         
41142         
41143         if(this.boxLabel){
41144             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41145         //    viewEl.on('click', this.onClick,  this); 
41146         }
41147          if(this.checked){
41148             this.el.dom.checked =   'checked' ;
41149         }
41150          
41151     } 
41152     
41153     
41154 });//<script type="text/javascript">
41155
41156 /*
41157  * Based  Ext JS Library 1.1.1
41158  * Copyright(c) 2006-2007, Ext JS, LLC.
41159  * LGPL
41160  *
41161  */
41162  
41163 /**
41164  * @class Roo.HtmlEditorCore
41165  * @extends Roo.Component
41166  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41167  *
41168  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41169  */
41170
41171 Roo.HtmlEditorCore = function(config){
41172     
41173     
41174     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41175     
41176     
41177     this.addEvents({
41178         /**
41179          * @event initialize
41180          * Fires when the editor is fully initialized (including the iframe)
41181          * @param {Roo.HtmlEditorCore} this
41182          */
41183         initialize: true,
41184         /**
41185          * @event activate
41186          * Fires when the editor is first receives the focus. Any insertion must wait
41187          * until after this event.
41188          * @param {Roo.HtmlEditorCore} this
41189          */
41190         activate: true,
41191          /**
41192          * @event beforesync
41193          * Fires before the textarea is updated with content from the editor iframe. Return false
41194          * to cancel the sync.
41195          * @param {Roo.HtmlEditorCore} this
41196          * @param {String} html
41197          */
41198         beforesync: true,
41199          /**
41200          * @event beforepush
41201          * Fires before the iframe editor is updated with content from the textarea. Return false
41202          * to cancel the push.
41203          * @param {Roo.HtmlEditorCore} this
41204          * @param {String} html
41205          */
41206         beforepush: true,
41207          /**
41208          * @event sync
41209          * Fires when the textarea is updated with content from the editor iframe.
41210          * @param {Roo.HtmlEditorCore} this
41211          * @param {String} html
41212          */
41213         sync: true,
41214          /**
41215          * @event push
41216          * Fires when the iframe editor is updated with content from the textarea.
41217          * @param {Roo.HtmlEditorCore} this
41218          * @param {String} html
41219          */
41220         push: true,
41221         
41222         /**
41223          * @event editorevent
41224          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41225          * @param {Roo.HtmlEditorCore} this
41226          */
41227         editorevent: true
41228         
41229     });
41230     
41231     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41232     
41233     // defaults : white / black...
41234     this.applyBlacklists();
41235     
41236     
41237     
41238 };
41239
41240
41241 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41242
41243
41244      /**
41245      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41246      */
41247     
41248     owner : false,
41249     
41250      /**
41251      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41252      *                        Roo.resizable.
41253      */
41254     resizable : false,
41255      /**
41256      * @cfg {Number} height (in pixels)
41257      */   
41258     height: 300,
41259    /**
41260      * @cfg {Number} width (in pixels)
41261      */   
41262     width: 500,
41263     
41264     /**
41265      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41266      * 
41267      */
41268     stylesheets: false,
41269     
41270     // id of frame..
41271     frameId: false,
41272     
41273     // private properties
41274     validationEvent : false,
41275     deferHeight: true,
41276     initialized : false,
41277     activated : false,
41278     sourceEditMode : false,
41279     onFocus : Roo.emptyFn,
41280     iframePad:3,
41281     hideMode:'offsets',
41282     
41283     clearUp: true,
41284     
41285     // blacklist + whitelisted elements..
41286     black: false,
41287     white: false,
41288      
41289     
41290
41291     /**
41292      * Protected method that will not generally be called directly. It
41293      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41294      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41295      */
41296     getDocMarkup : function(){
41297         // body styles..
41298         var st = '';
41299         
41300         // inherit styels from page...?? 
41301         if (this.stylesheets === false) {
41302             
41303             Roo.get(document.head).select('style').each(function(node) {
41304                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41305             });
41306             
41307             Roo.get(document.head).select('link').each(function(node) { 
41308                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41309             });
41310             
41311         } else if (!this.stylesheets.length) {
41312                 // simple..
41313                 st = '<style type="text/css">' +
41314                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41315                    '</style>';
41316         } else { 
41317             
41318         }
41319         
41320         st +=  '<style type="text/css">' +
41321             'IMG { cursor: pointer } ' +
41322         '</style>';
41323
41324         
41325         return '<html><head>' + st  +
41326             //<style type="text/css">' +
41327             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41328             //'</style>' +
41329             ' </head><body class="roo-htmleditor-body"></body></html>';
41330     },
41331
41332     // private
41333     onRender : function(ct, position)
41334     {
41335         var _t = this;
41336         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41337         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41338         
41339         
41340         this.el.dom.style.border = '0 none';
41341         this.el.dom.setAttribute('tabIndex', -1);
41342         this.el.addClass('x-hidden hide');
41343         
41344         
41345         
41346         if(Roo.isIE){ // fix IE 1px bogus margin
41347             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41348         }
41349        
41350         
41351         this.frameId = Roo.id();
41352         
41353          
41354         
41355         var iframe = this.owner.wrap.createChild({
41356             tag: 'iframe',
41357             cls: 'form-control', // bootstrap..
41358             id: this.frameId,
41359             name: this.frameId,
41360             frameBorder : 'no',
41361             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41362         }, this.el
41363         );
41364         
41365         
41366         this.iframe = iframe.dom;
41367
41368          this.assignDocWin();
41369         
41370         this.doc.designMode = 'on';
41371        
41372         this.doc.open();
41373         this.doc.write(this.getDocMarkup());
41374         this.doc.close();
41375
41376         
41377         var task = { // must defer to wait for browser to be ready
41378             run : function(){
41379                 //console.log("run task?" + this.doc.readyState);
41380                 this.assignDocWin();
41381                 if(this.doc.body || this.doc.readyState == 'complete'){
41382                     try {
41383                         this.doc.designMode="on";
41384                     } catch (e) {
41385                         return;
41386                     }
41387                     Roo.TaskMgr.stop(task);
41388                     this.initEditor.defer(10, this);
41389                 }
41390             },
41391             interval : 10,
41392             duration: 10000,
41393             scope: this
41394         };
41395         Roo.TaskMgr.start(task);
41396
41397     },
41398
41399     // private
41400     onResize : function(w, h)
41401     {
41402          Roo.log('resize: ' +w + ',' + h );
41403         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41404         if(!this.iframe){
41405             return;
41406         }
41407         if(typeof w == 'number'){
41408             
41409             this.iframe.style.width = w + 'px';
41410         }
41411         if(typeof h == 'number'){
41412             
41413             this.iframe.style.height = h + 'px';
41414             if(this.doc){
41415                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41416             }
41417         }
41418         
41419     },
41420
41421     /**
41422      * Toggles the editor between standard and source edit mode.
41423      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41424      */
41425     toggleSourceEdit : function(sourceEditMode){
41426         
41427         this.sourceEditMode = sourceEditMode === true;
41428         
41429         if(this.sourceEditMode){
41430  
41431             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41432             
41433         }else{
41434             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41435             //this.iframe.className = '';
41436             this.deferFocus();
41437         }
41438         //this.setSize(this.owner.wrap.getSize());
41439         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41440     },
41441
41442     
41443   
41444
41445     /**
41446      * Protected method that will not generally be called directly. If you need/want
41447      * custom HTML cleanup, this is the method you should override.
41448      * @param {String} html The HTML to be cleaned
41449      * return {String} The cleaned HTML
41450      */
41451     cleanHtml : function(html){
41452         html = String(html);
41453         if(html.length > 5){
41454             if(Roo.isSafari){ // strip safari nonsense
41455                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41456             }
41457         }
41458         if(html == '&nbsp;'){
41459             html = '';
41460         }
41461         return html;
41462     },
41463
41464     /**
41465      * HTML Editor -> Textarea
41466      * Protected method that will not generally be called directly. Syncs the contents
41467      * of the editor iframe with the textarea.
41468      */
41469     syncValue : function(){
41470         if(this.initialized){
41471             var bd = (this.doc.body || this.doc.documentElement);
41472             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41473             var html = bd.innerHTML;
41474             if(Roo.isSafari){
41475                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41476                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41477                 if(m && m[1]){
41478                     html = '<div style="'+m[0]+'">' + html + '</div>';
41479                 }
41480             }
41481             html = this.cleanHtml(html);
41482             // fix up the special chars.. normaly like back quotes in word...
41483             // however we do not want to do this with chinese..
41484             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41485                 var cc = b.charCodeAt();
41486                 if (
41487                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41488                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41489                     (cc >= 0xf900 && cc < 0xfb00 )
41490                 ) {
41491                         return b;
41492                 }
41493                 return "&#"+cc+";" 
41494             });
41495             if(this.owner.fireEvent('beforesync', this, html) !== false){
41496                 this.el.dom.value = html;
41497                 this.owner.fireEvent('sync', this, html);
41498             }
41499         }
41500     },
41501
41502     /**
41503      * Protected method that will not generally be called directly. Pushes the value of the textarea
41504      * into the iframe editor.
41505      */
41506     pushValue : function(){
41507         if(this.initialized){
41508             var v = this.el.dom.value.trim();
41509             
41510 //            if(v.length < 1){
41511 //                v = '&#160;';
41512 //            }
41513             
41514             if(this.owner.fireEvent('beforepush', this, v) !== false){
41515                 var d = (this.doc.body || this.doc.documentElement);
41516                 d.innerHTML = v;
41517                 this.cleanUpPaste();
41518                 this.el.dom.value = d.innerHTML;
41519                 this.owner.fireEvent('push', this, v);
41520             }
41521         }
41522     },
41523
41524     // private
41525     deferFocus : function(){
41526         this.focus.defer(10, this);
41527     },
41528
41529     // doc'ed in Field
41530     focus : function(){
41531         if(this.win && !this.sourceEditMode){
41532             this.win.focus();
41533         }else{
41534             this.el.focus();
41535         }
41536     },
41537     
41538     assignDocWin: function()
41539     {
41540         var iframe = this.iframe;
41541         
41542          if(Roo.isIE){
41543             this.doc = iframe.contentWindow.document;
41544             this.win = iframe.contentWindow;
41545         } else {
41546 //            if (!Roo.get(this.frameId)) {
41547 //                return;
41548 //            }
41549 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41550 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41551             
41552             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41553                 return;
41554             }
41555             
41556             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41557             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41558         }
41559     },
41560     
41561     // private
41562     initEditor : function(){
41563         //console.log("INIT EDITOR");
41564         this.assignDocWin();
41565         
41566         
41567         
41568         this.doc.designMode="on";
41569         this.doc.open();
41570         this.doc.write(this.getDocMarkup());
41571         this.doc.close();
41572         
41573         var dbody = (this.doc.body || this.doc.documentElement);
41574         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41575         // this copies styles from the containing element into thsi one..
41576         // not sure why we need all of this..
41577         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41578         
41579         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41580         //ss['background-attachment'] = 'fixed'; // w3c
41581         dbody.bgProperties = 'fixed'; // ie
41582         //Roo.DomHelper.applyStyles(dbody, ss);
41583         Roo.EventManager.on(this.doc, {
41584             //'mousedown': this.onEditorEvent,
41585             'mouseup': this.onEditorEvent,
41586             'dblclick': this.onEditorEvent,
41587             'click': this.onEditorEvent,
41588             'keyup': this.onEditorEvent,
41589             buffer:100,
41590             scope: this
41591         });
41592         if(Roo.isGecko){
41593             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41594         }
41595         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41596             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41597         }
41598         this.initialized = true;
41599
41600         this.owner.fireEvent('initialize', this);
41601         this.pushValue();
41602     },
41603
41604     // private
41605     onDestroy : function(){
41606         
41607         
41608         
41609         if(this.rendered){
41610             
41611             //for (var i =0; i < this.toolbars.length;i++) {
41612             //    // fixme - ask toolbars for heights?
41613             //    this.toolbars[i].onDestroy();
41614            // }
41615             
41616             //this.wrap.dom.innerHTML = '';
41617             //this.wrap.remove();
41618         }
41619     },
41620
41621     // private
41622     onFirstFocus : function(){
41623         
41624         this.assignDocWin();
41625         
41626         
41627         this.activated = true;
41628          
41629     
41630         if(Roo.isGecko){ // prevent silly gecko errors
41631             this.win.focus();
41632             var s = this.win.getSelection();
41633             if(!s.focusNode || s.focusNode.nodeType != 3){
41634                 var r = s.getRangeAt(0);
41635                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41636                 r.collapse(true);
41637                 this.deferFocus();
41638             }
41639             try{
41640                 this.execCmd('useCSS', true);
41641                 this.execCmd('styleWithCSS', false);
41642             }catch(e){}
41643         }
41644         this.owner.fireEvent('activate', this);
41645     },
41646
41647     // private
41648     adjustFont: function(btn){
41649         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41650         //if(Roo.isSafari){ // safari
41651         //    adjust *= 2;
41652        // }
41653         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41654         if(Roo.isSafari){ // safari
41655             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41656             v =  (v < 10) ? 10 : v;
41657             v =  (v > 48) ? 48 : v;
41658             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41659             
41660         }
41661         
41662         
41663         v = Math.max(1, v+adjust);
41664         
41665         this.execCmd('FontSize', v  );
41666     },
41667
41668     onEditorEvent : function(e)
41669     {
41670         this.owner.fireEvent('editorevent', this, e);
41671       //  this.updateToolbar();
41672         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41673     },
41674
41675     insertTag : function(tg)
41676     {
41677         // could be a bit smarter... -> wrap the current selected tRoo..
41678         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41679             
41680             range = this.createRange(this.getSelection());
41681             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41682             wrappingNode.appendChild(range.extractContents());
41683             range.insertNode(wrappingNode);
41684
41685             return;
41686             
41687             
41688             
41689         }
41690         this.execCmd("formatblock",   tg);
41691         
41692     },
41693     
41694     insertText : function(txt)
41695     {
41696         
41697         
41698         var range = this.createRange();
41699         range.deleteContents();
41700                //alert(Sender.getAttribute('label'));
41701                
41702         range.insertNode(this.doc.createTextNode(txt));
41703     } ,
41704     
41705      
41706
41707     /**
41708      * Executes a Midas editor command on the editor document and performs necessary focus and
41709      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41710      * @param {String} cmd The Midas command
41711      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41712      */
41713     relayCmd : function(cmd, value){
41714         this.win.focus();
41715         this.execCmd(cmd, value);
41716         this.owner.fireEvent('editorevent', this);
41717         //this.updateToolbar();
41718         this.owner.deferFocus();
41719     },
41720
41721     /**
41722      * Executes a Midas editor command directly on the editor document.
41723      * For visual commands, you should use {@link #relayCmd} instead.
41724      * <b>This should only be called after the editor is initialized.</b>
41725      * @param {String} cmd The Midas command
41726      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41727      */
41728     execCmd : function(cmd, value){
41729         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41730         this.syncValue();
41731     },
41732  
41733  
41734    
41735     /**
41736      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41737      * to insert tRoo.
41738      * @param {String} text | dom node.. 
41739      */
41740     insertAtCursor : function(text)
41741     {
41742         
41743         
41744         
41745         if(!this.activated){
41746             return;
41747         }
41748         /*
41749         if(Roo.isIE){
41750             this.win.focus();
41751             var r = this.doc.selection.createRange();
41752             if(r){
41753                 r.collapse(true);
41754                 r.pasteHTML(text);
41755                 this.syncValue();
41756                 this.deferFocus();
41757             
41758             }
41759             return;
41760         }
41761         */
41762         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41763             this.win.focus();
41764             
41765             
41766             // from jquery ui (MIT licenced)
41767             var range, node;
41768             var win = this.win;
41769             
41770             if (win.getSelection && win.getSelection().getRangeAt) {
41771                 range = win.getSelection().getRangeAt(0);
41772                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41773                 range.insertNode(node);
41774             } else if (win.document.selection && win.document.selection.createRange) {
41775                 // no firefox support
41776                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41777                 win.document.selection.createRange().pasteHTML(txt);
41778             } else {
41779                 // no firefox support
41780                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41781                 this.execCmd('InsertHTML', txt);
41782             } 
41783             
41784             this.syncValue();
41785             
41786             this.deferFocus();
41787         }
41788     },
41789  // private
41790     mozKeyPress : function(e){
41791         if(e.ctrlKey){
41792             var c = e.getCharCode(), cmd;
41793           
41794             if(c > 0){
41795                 c = String.fromCharCode(c).toLowerCase();
41796                 switch(c){
41797                     case 'b':
41798                         cmd = 'bold';
41799                         break;
41800                     case 'i':
41801                         cmd = 'italic';
41802                         break;
41803                     
41804                     case 'u':
41805                         cmd = 'underline';
41806                         break;
41807                     
41808                     case 'v':
41809                         this.cleanUpPaste.defer(100, this);
41810                         return;
41811                         
41812                 }
41813                 if(cmd){
41814                     this.win.focus();
41815                     this.execCmd(cmd);
41816                     this.deferFocus();
41817                     e.preventDefault();
41818                 }
41819                 
41820             }
41821         }
41822     },
41823
41824     // private
41825     fixKeys : function(){ // load time branching for fastest keydown performance
41826         if(Roo.isIE){
41827             return function(e){
41828                 var k = e.getKey(), r;
41829                 if(k == e.TAB){
41830                     e.stopEvent();
41831                     r = this.doc.selection.createRange();
41832                     if(r){
41833                         r.collapse(true);
41834                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41835                         this.deferFocus();
41836                     }
41837                     return;
41838                 }
41839                 
41840                 if(k == e.ENTER){
41841                     r = this.doc.selection.createRange();
41842                     if(r){
41843                         var target = r.parentElement();
41844                         if(!target || target.tagName.toLowerCase() != 'li'){
41845                             e.stopEvent();
41846                             r.pasteHTML('<br />');
41847                             r.collapse(false);
41848                             r.select();
41849                         }
41850                     }
41851                 }
41852                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41853                     this.cleanUpPaste.defer(100, this);
41854                     return;
41855                 }
41856                 
41857                 
41858             };
41859         }else if(Roo.isOpera){
41860             return function(e){
41861                 var k = e.getKey();
41862                 if(k == e.TAB){
41863                     e.stopEvent();
41864                     this.win.focus();
41865                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41866                     this.deferFocus();
41867                 }
41868                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41869                     this.cleanUpPaste.defer(100, this);
41870                     return;
41871                 }
41872                 
41873             };
41874         }else if(Roo.isSafari){
41875             return function(e){
41876                 var k = e.getKey();
41877                 
41878                 if(k == e.TAB){
41879                     e.stopEvent();
41880                     this.execCmd('InsertText','\t');
41881                     this.deferFocus();
41882                     return;
41883                 }
41884                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41885                     this.cleanUpPaste.defer(100, this);
41886                     return;
41887                 }
41888                 
41889              };
41890         }
41891     }(),
41892     
41893     getAllAncestors: function()
41894     {
41895         var p = this.getSelectedNode();
41896         var a = [];
41897         if (!p) {
41898             a.push(p); // push blank onto stack..
41899             p = this.getParentElement();
41900         }
41901         
41902         
41903         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41904             a.push(p);
41905             p = p.parentNode;
41906         }
41907         a.push(this.doc.body);
41908         return a;
41909     },
41910     lastSel : false,
41911     lastSelNode : false,
41912     
41913     
41914     getSelection : function() 
41915     {
41916         this.assignDocWin();
41917         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41918     },
41919     
41920     getSelectedNode: function() 
41921     {
41922         // this may only work on Gecko!!!
41923         
41924         // should we cache this!!!!
41925         
41926         
41927         
41928          
41929         var range = this.createRange(this.getSelection()).cloneRange();
41930         
41931         if (Roo.isIE) {
41932             var parent = range.parentElement();
41933             while (true) {
41934                 var testRange = range.duplicate();
41935                 testRange.moveToElementText(parent);
41936                 if (testRange.inRange(range)) {
41937                     break;
41938                 }
41939                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41940                     break;
41941                 }
41942                 parent = parent.parentElement;
41943             }
41944             return parent;
41945         }
41946         
41947         // is ancestor a text element.
41948         var ac =  range.commonAncestorContainer;
41949         if (ac.nodeType == 3) {
41950             ac = ac.parentNode;
41951         }
41952         
41953         var ar = ac.childNodes;
41954          
41955         var nodes = [];
41956         var other_nodes = [];
41957         var has_other_nodes = false;
41958         for (var i=0;i<ar.length;i++) {
41959             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41960                 continue;
41961             }
41962             // fullly contained node.
41963             
41964             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41965                 nodes.push(ar[i]);
41966                 continue;
41967             }
41968             
41969             // probably selected..
41970             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41971                 other_nodes.push(ar[i]);
41972                 continue;
41973             }
41974             // outer..
41975             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41976                 continue;
41977             }
41978             
41979             
41980             has_other_nodes = true;
41981         }
41982         if (!nodes.length && other_nodes.length) {
41983             nodes= other_nodes;
41984         }
41985         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41986             return false;
41987         }
41988         
41989         return nodes[0];
41990     },
41991     createRange: function(sel)
41992     {
41993         // this has strange effects when using with 
41994         // top toolbar - not sure if it's a great idea.
41995         //this.editor.contentWindow.focus();
41996         if (typeof sel != "undefined") {
41997             try {
41998                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41999             } catch(e) {
42000                 return this.doc.createRange();
42001             }
42002         } else {
42003             return this.doc.createRange();
42004         }
42005     },
42006     getParentElement: function()
42007     {
42008         
42009         this.assignDocWin();
42010         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
42011         
42012         var range = this.createRange(sel);
42013          
42014         try {
42015             var p = range.commonAncestorContainer;
42016             while (p.nodeType == 3) { // text node
42017                 p = p.parentNode;
42018             }
42019             return p;
42020         } catch (e) {
42021             return null;
42022         }
42023     
42024     },
42025     /***
42026      *
42027      * Range intersection.. the hard stuff...
42028      *  '-1' = before
42029      *  '0' = hits..
42030      *  '1' = after.
42031      *         [ -- selected range --- ]
42032      *   [fail]                        [fail]
42033      *
42034      *    basically..
42035      *      if end is before start or  hits it. fail.
42036      *      if start is after end or hits it fail.
42037      *
42038      *   if either hits (but other is outside. - then it's not 
42039      *   
42040      *    
42041      **/
42042     
42043     
42044     // @see http://www.thismuchiknow.co.uk/?p=64.
42045     rangeIntersectsNode : function(range, node)
42046     {
42047         var nodeRange = node.ownerDocument.createRange();
42048         try {
42049             nodeRange.selectNode(node);
42050         } catch (e) {
42051             nodeRange.selectNodeContents(node);
42052         }
42053     
42054         var rangeStartRange = range.cloneRange();
42055         rangeStartRange.collapse(true);
42056     
42057         var rangeEndRange = range.cloneRange();
42058         rangeEndRange.collapse(false);
42059     
42060         var nodeStartRange = nodeRange.cloneRange();
42061         nodeStartRange.collapse(true);
42062     
42063         var nodeEndRange = nodeRange.cloneRange();
42064         nodeEndRange.collapse(false);
42065     
42066         return rangeStartRange.compareBoundaryPoints(
42067                  Range.START_TO_START, nodeEndRange) == -1 &&
42068                rangeEndRange.compareBoundaryPoints(
42069                  Range.START_TO_START, nodeStartRange) == 1;
42070         
42071          
42072     },
42073     rangeCompareNode : function(range, node)
42074     {
42075         var nodeRange = node.ownerDocument.createRange();
42076         try {
42077             nodeRange.selectNode(node);
42078         } catch (e) {
42079             nodeRange.selectNodeContents(node);
42080         }
42081         
42082         
42083         range.collapse(true);
42084     
42085         nodeRange.collapse(true);
42086      
42087         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42088         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42089          
42090         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42091         
42092         var nodeIsBefore   =  ss == 1;
42093         var nodeIsAfter    = ee == -1;
42094         
42095         if (nodeIsBefore && nodeIsAfter) {
42096             return 0; // outer
42097         }
42098         if (!nodeIsBefore && nodeIsAfter) {
42099             return 1; //right trailed.
42100         }
42101         
42102         if (nodeIsBefore && !nodeIsAfter) {
42103             return 2;  // left trailed.
42104         }
42105         // fully contined.
42106         return 3;
42107     },
42108
42109     // private? - in a new class?
42110     cleanUpPaste :  function()
42111     {
42112         // cleans up the whole document..
42113         Roo.log('cleanuppaste');
42114         
42115         this.cleanUpChildren(this.doc.body);
42116         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42117         if (clean != this.doc.body.innerHTML) {
42118             this.doc.body.innerHTML = clean;
42119         }
42120         
42121     },
42122     
42123     cleanWordChars : function(input) {// change the chars to hex code
42124         var he = Roo.HtmlEditorCore;
42125         
42126         var output = input;
42127         Roo.each(he.swapCodes, function(sw) { 
42128             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42129             
42130             output = output.replace(swapper, sw[1]);
42131         });
42132         
42133         return output;
42134     },
42135     
42136     
42137     cleanUpChildren : function (n)
42138     {
42139         if (!n.childNodes.length) {
42140             return;
42141         }
42142         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42143            this.cleanUpChild(n.childNodes[i]);
42144         }
42145     },
42146     
42147     
42148         
42149     
42150     cleanUpChild : function (node)
42151     {
42152         var ed = this;
42153         //console.log(node);
42154         if (node.nodeName == "#text") {
42155             // clean up silly Windows -- stuff?
42156             return; 
42157         }
42158         if (node.nodeName == "#comment") {
42159             node.parentNode.removeChild(node);
42160             // clean up silly Windows -- stuff?
42161             return; 
42162         }
42163         var lcname = node.tagName.toLowerCase();
42164         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42165         // whitelist of tags..
42166         
42167         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42168             // remove node.
42169             node.parentNode.removeChild(node);
42170             return;
42171             
42172         }
42173         
42174         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42175         
42176         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42177         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42178         
42179         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42180         //    remove_keep_children = true;
42181         //}
42182         
42183         if (remove_keep_children) {
42184             this.cleanUpChildren(node);
42185             // inserts everything just before this node...
42186             while (node.childNodes.length) {
42187                 var cn = node.childNodes[0];
42188                 node.removeChild(cn);
42189                 node.parentNode.insertBefore(cn, node);
42190             }
42191             node.parentNode.removeChild(node);
42192             return;
42193         }
42194         
42195         if (!node.attributes || !node.attributes.length) {
42196             this.cleanUpChildren(node);
42197             return;
42198         }
42199         
42200         function cleanAttr(n,v)
42201         {
42202             
42203             if (v.match(/^\./) || v.match(/^\//)) {
42204                 return;
42205             }
42206             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42207                 return;
42208             }
42209             if (v.match(/^#/)) {
42210                 return;
42211             }
42212 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42213             node.removeAttribute(n);
42214             
42215         }
42216         
42217         var cwhite = this.cwhite;
42218         var cblack = this.cblack;
42219             
42220         function cleanStyle(n,v)
42221         {
42222             if (v.match(/expression/)) { //XSS?? should we even bother..
42223                 node.removeAttribute(n);
42224                 return;
42225             }
42226             
42227             var parts = v.split(/;/);
42228             var clean = [];
42229             
42230             Roo.each(parts, function(p) {
42231                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42232                 if (!p.length) {
42233                     return true;
42234                 }
42235                 var l = p.split(':').shift().replace(/\s+/g,'');
42236                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42237                 
42238                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42239 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42240                     //node.removeAttribute(n);
42241                     return true;
42242                 }
42243                 //Roo.log()
42244                 // only allow 'c whitelisted system attributes'
42245                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42246 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42247                     //node.removeAttribute(n);
42248                     return true;
42249                 }
42250                 
42251                 
42252                  
42253                 
42254                 clean.push(p);
42255                 return true;
42256             });
42257             if (clean.length) { 
42258                 node.setAttribute(n, clean.join(';'));
42259             } else {
42260                 node.removeAttribute(n);
42261             }
42262             
42263         }
42264         
42265         
42266         for (var i = node.attributes.length-1; i > -1 ; i--) {
42267             var a = node.attributes[i];
42268             //console.log(a);
42269             
42270             if (a.name.toLowerCase().substr(0,2)=='on')  {
42271                 node.removeAttribute(a.name);
42272                 continue;
42273             }
42274             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42275                 node.removeAttribute(a.name);
42276                 continue;
42277             }
42278             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42279                 cleanAttr(a.name,a.value); // fixme..
42280                 continue;
42281             }
42282             if (a.name == 'style') {
42283                 cleanStyle(a.name,a.value);
42284                 continue;
42285             }
42286             /// clean up MS crap..
42287             // tecnically this should be a list of valid class'es..
42288             
42289             
42290             if (a.name == 'class') {
42291                 if (a.value.match(/^Mso/)) {
42292                     node.className = '';
42293                 }
42294                 
42295                 if (a.value.match(/body/)) {
42296                     node.className = '';
42297                 }
42298                 continue;
42299             }
42300             
42301             // style cleanup!?
42302             // class cleanup?
42303             
42304         }
42305         
42306         
42307         this.cleanUpChildren(node);
42308         
42309         
42310     },
42311     
42312     /**
42313      * Clean up MS wordisms...
42314      */
42315     cleanWord : function(node)
42316     {
42317         
42318         
42319         if (!node) {
42320             this.cleanWord(this.doc.body);
42321             return;
42322         }
42323         if (node.nodeName == "#text") {
42324             // clean up silly Windows -- stuff?
42325             return; 
42326         }
42327         if (node.nodeName == "#comment") {
42328             node.parentNode.removeChild(node);
42329             // clean up silly Windows -- stuff?
42330             return; 
42331         }
42332         
42333         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42334             node.parentNode.removeChild(node);
42335             return;
42336         }
42337         
42338         // remove - but keep children..
42339         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42340             while (node.childNodes.length) {
42341                 var cn = node.childNodes[0];
42342                 node.removeChild(cn);
42343                 node.parentNode.insertBefore(cn, node);
42344             }
42345             node.parentNode.removeChild(node);
42346             this.iterateChildren(node, this.cleanWord);
42347             return;
42348         }
42349         // clean styles
42350         if (node.className.length) {
42351             
42352             var cn = node.className.split(/\W+/);
42353             var cna = [];
42354             Roo.each(cn, function(cls) {
42355                 if (cls.match(/Mso[a-zA-Z]+/)) {
42356                     return;
42357                 }
42358                 cna.push(cls);
42359             });
42360             node.className = cna.length ? cna.join(' ') : '';
42361             if (!cna.length) {
42362                 node.removeAttribute("class");
42363             }
42364         }
42365         
42366         if (node.hasAttribute("lang")) {
42367             node.removeAttribute("lang");
42368         }
42369         
42370         if (node.hasAttribute("style")) {
42371             
42372             var styles = node.getAttribute("style").split(";");
42373             var nstyle = [];
42374             Roo.each(styles, function(s) {
42375                 if (!s.match(/:/)) {
42376                     return;
42377                 }
42378                 var kv = s.split(":");
42379                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42380                     return;
42381                 }
42382                 // what ever is left... we allow.
42383                 nstyle.push(s);
42384             });
42385             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42386             if (!nstyle.length) {
42387                 node.removeAttribute('style');
42388             }
42389         }
42390         this.iterateChildren(node, this.cleanWord);
42391         
42392         
42393         
42394     },
42395     /**
42396      * iterateChildren of a Node, calling fn each time, using this as the scole..
42397      * @param {DomNode} node node to iterate children of.
42398      * @param {Function} fn method of this class to call on each item.
42399      */
42400     iterateChildren : function(node, fn)
42401     {
42402         if (!node.childNodes.length) {
42403                 return;
42404         }
42405         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42406            fn.call(this, node.childNodes[i])
42407         }
42408     },
42409     
42410     
42411     /**
42412      * cleanTableWidths.
42413      *
42414      * Quite often pasting from word etc.. results in tables with column and widths.
42415      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42416      *
42417      */
42418     cleanTableWidths : function(node)
42419     {
42420          
42421          
42422         if (!node) {
42423             this.cleanTableWidths(this.doc.body);
42424             return;
42425         }
42426         
42427         // ignore list...
42428         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42429             return; 
42430         }
42431         Roo.log(node.tagName);
42432         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42433             this.iterateChildren(node, this.cleanTableWidths);
42434             return;
42435         }
42436         if (node.hasAttribute('width')) {
42437             node.removeAttribute('width');
42438         }
42439         
42440          
42441         if (node.hasAttribute("style")) {
42442             // pretty basic...
42443             
42444             var styles = node.getAttribute("style").split(";");
42445             var nstyle = [];
42446             Roo.each(styles, function(s) {
42447                 if (!s.match(/:/)) {
42448                     return;
42449                 }
42450                 var kv = s.split(":");
42451                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42452                     return;
42453                 }
42454                 // what ever is left... we allow.
42455                 nstyle.push(s);
42456             });
42457             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42458             if (!nstyle.length) {
42459                 node.removeAttribute('style');
42460             }
42461         }
42462         
42463         this.iterateChildren(node, this.cleanTableWidths);
42464         
42465         
42466     },
42467     
42468     
42469     
42470     
42471     domToHTML : function(currentElement, depth, nopadtext) {
42472         
42473         depth = depth || 0;
42474         nopadtext = nopadtext || false;
42475     
42476         if (!currentElement) {
42477             return this.domToHTML(this.doc.body);
42478         }
42479         
42480         //Roo.log(currentElement);
42481         var j;
42482         var allText = false;
42483         var nodeName = currentElement.nodeName;
42484         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42485         
42486         if  (nodeName == '#text') {
42487             
42488             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42489         }
42490         
42491         
42492         var ret = '';
42493         if (nodeName != 'BODY') {
42494              
42495             var i = 0;
42496             // Prints the node tagName, such as <A>, <IMG>, etc
42497             if (tagName) {
42498                 var attr = [];
42499                 for(i = 0; i < currentElement.attributes.length;i++) {
42500                     // quoting?
42501                     var aname = currentElement.attributes.item(i).name;
42502                     if (!currentElement.attributes.item(i).value.length) {
42503                         continue;
42504                     }
42505                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42506                 }
42507                 
42508                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42509             } 
42510             else {
42511                 
42512                 // eack
42513             }
42514         } else {
42515             tagName = false;
42516         }
42517         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42518             return ret;
42519         }
42520         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42521             nopadtext = true;
42522         }
42523         
42524         
42525         // Traverse the tree
42526         i = 0;
42527         var currentElementChild = currentElement.childNodes.item(i);
42528         var allText = true;
42529         var innerHTML  = '';
42530         lastnode = '';
42531         while (currentElementChild) {
42532             // Formatting code (indent the tree so it looks nice on the screen)
42533             var nopad = nopadtext;
42534             if (lastnode == 'SPAN') {
42535                 nopad  = true;
42536             }
42537             // text
42538             if  (currentElementChild.nodeName == '#text') {
42539                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42540                 toadd = nopadtext ? toadd : toadd.trim();
42541                 if (!nopad && toadd.length > 80) {
42542                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42543                 }
42544                 innerHTML  += toadd;
42545                 
42546                 i++;
42547                 currentElementChild = currentElement.childNodes.item(i);
42548                 lastNode = '';
42549                 continue;
42550             }
42551             allText = false;
42552             
42553             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42554                 
42555             // Recursively traverse the tree structure of the child node
42556             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42557             lastnode = currentElementChild.nodeName;
42558             i++;
42559             currentElementChild=currentElement.childNodes.item(i);
42560         }
42561         
42562         ret += innerHTML;
42563         
42564         if (!allText) {
42565                 // The remaining code is mostly for formatting the tree
42566             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42567         }
42568         
42569         
42570         if (tagName) {
42571             ret+= "</"+tagName+">";
42572         }
42573         return ret;
42574         
42575     },
42576         
42577     applyBlacklists : function()
42578     {
42579         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42580         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42581         
42582         this.white = [];
42583         this.black = [];
42584         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42585             if (b.indexOf(tag) > -1) {
42586                 return;
42587             }
42588             this.white.push(tag);
42589             
42590         }, this);
42591         
42592         Roo.each(w, function(tag) {
42593             if (b.indexOf(tag) > -1) {
42594                 return;
42595             }
42596             if (this.white.indexOf(tag) > -1) {
42597                 return;
42598             }
42599             this.white.push(tag);
42600             
42601         }, this);
42602         
42603         
42604         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42605             if (w.indexOf(tag) > -1) {
42606                 return;
42607             }
42608             this.black.push(tag);
42609             
42610         }, this);
42611         
42612         Roo.each(b, function(tag) {
42613             if (w.indexOf(tag) > -1) {
42614                 return;
42615             }
42616             if (this.black.indexOf(tag) > -1) {
42617                 return;
42618             }
42619             this.black.push(tag);
42620             
42621         }, this);
42622         
42623         
42624         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42625         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42626         
42627         this.cwhite = [];
42628         this.cblack = [];
42629         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42630             if (b.indexOf(tag) > -1) {
42631                 return;
42632             }
42633             this.cwhite.push(tag);
42634             
42635         }, this);
42636         
42637         Roo.each(w, function(tag) {
42638             if (b.indexOf(tag) > -1) {
42639                 return;
42640             }
42641             if (this.cwhite.indexOf(tag) > -1) {
42642                 return;
42643             }
42644             this.cwhite.push(tag);
42645             
42646         }, this);
42647         
42648         
42649         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42650             if (w.indexOf(tag) > -1) {
42651                 return;
42652             }
42653             this.cblack.push(tag);
42654             
42655         }, this);
42656         
42657         Roo.each(b, function(tag) {
42658             if (w.indexOf(tag) > -1) {
42659                 return;
42660             }
42661             if (this.cblack.indexOf(tag) > -1) {
42662                 return;
42663             }
42664             this.cblack.push(tag);
42665             
42666         }, this);
42667     },
42668     
42669     setStylesheets : function(stylesheets)
42670     {
42671         if(typeof(stylesheets) == 'string'){
42672             Roo.get(this.iframe.contentDocument.head).createChild({
42673                 tag : 'link',
42674                 rel : 'stylesheet',
42675                 type : 'text/css',
42676                 href : stylesheets
42677             });
42678             
42679             return;
42680         }
42681         var _this = this;
42682      
42683         Roo.each(stylesheets, function(s) {
42684             if(!s.length){
42685                 return;
42686             }
42687             
42688             Roo.get(_this.iframe.contentDocument.head).createChild({
42689                 tag : 'link',
42690                 rel : 'stylesheet',
42691                 type : 'text/css',
42692                 href : s
42693             });
42694         });
42695
42696         
42697     },
42698     
42699     removeStylesheets : function()
42700     {
42701         var _this = this;
42702         
42703         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42704             s.remove();
42705         });
42706     }
42707     
42708     // hide stuff that is not compatible
42709     /**
42710      * @event blur
42711      * @hide
42712      */
42713     /**
42714      * @event change
42715      * @hide
42716      */
42717     /**
42718      * @event focus
42719      * @hide
42720      */
42721     /**
42722      * @event specialkey
42723      * @hide
42724      */
42725     /**
42726      * @cfg {String} fieldClass @hide
42727      */
42728     /**
42729      * @cfg {String} focusClass @hide
42730      */
42731     /**
42732      * @cfg {String} autoCreate @hide
42733      */
42734     /**
42735      * @cfg {String} inputType @hide
42736      */
42737     /**
42738      * @cfg {String} invalidClass @hide
42739      */
42740     /**
42741      * @cfg {String} invalidText @hide
42742      */
42743     /**
42744      * @cfg {String} msgFx @hide
42745      */
42746     /**
42747      * @cfg {String} validateOnBlur @hide
42748      */
42749 });
42750
42751 Roo.HtmlEditorCore.white = [
42752         'area', 'br', 'img', 'input', 'hr', 'wbr',
42753         
42754        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42755        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42756        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42757        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42758        'table',   'ul',         'xmp', 
42759        
42760        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42761       'thead',   'tr', 
42762      
42763       'dir', 'menu', 'ol', 'ul', 'dl',
42764        
42765       'embed',  'object'
42766 ];
42767
42768
42769 Roo.HtmlEditorCore.black = [
42770     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42771         'applet', // 
42772         'base',   'basefont', 'bgsound', 'blink',  'body', 
42773         'frame',  'frameset', 'head',    'html',   'ilayer', 
42774         'iframe', 'layer',  'link',     'meta',    'object',   
42775         'script', 'style' ,'title',  'xml' // clean later..
42776 ];
42777 Roo.HtmlEditorCore.clean = [
42778     'script', 'style', 'title', 'xml'
42779 ];
42780 Roo.HtmlEditorCore.remove = [
42781     'font'
42782 ];
42783 // attributes..
42784
42785 Roo.HtmlEditorCore.ablack = [
42786     'on'
42787 ];
42788     
42789 Roo.HtmlEditorCore.aclean = [ 
42790     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42791 ];
42792
42793 // protocols..
42794 Roo.HtmlEditorCore.pwhite= [
42795         'http',  'https',  'mailto'
42796 ];
42797
42798 // white listed style attributes.
42799 Roo.HtmlEditorCore.cwhite= [
42800       //  'text-align', /// default is to allow most things..
42801       
42802          
42803 //        'font-size'//??
42804 ];
42805
42806 // black listed style attributes.
42807 Roo.HtmlEditorCore.cblack= [
42808       //  'font-size' -- this can be set by the project 
42809 ];
42810
42811
42812 Roo.HtmlEditorCore.swapCodes   =[ 
42813     [    8211, "--" ], 
42814     [    8212, "--" ], 
42815     [    8216,  "'" ],  
42816     [    8217, "'" ],  
42817     [    8220, '"' ],  
42818     [    8221, '"' ],  
42819     [    8226, "*" ],  
42820     [    8230, "..." ]
42821 ]; 
42822
42823     //<script type="text/javascript">
42824
42825 /*
42826  * Ext JS Library 1.1.1
42827  * Copyright(c) 2006-2007, Ext JS, LLC.
42828  * Licence LGPL
42829  * 
42830  */
42831  
42832  
42833 Roo.form.HtmlEditor = function(config){
42834     
42835     
42836     
42837     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42838     
42839     if (!this.toolbars) {
42840         this.toolbars = [];
42841     }
42842     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42843     
42844     
42845 };
42846
42847 /**
42848  * @class Roo.form.HtmlEditor
42849  * @extends Roo.form.Field
42850  * Provides a lightweight HTML Editor component.
42851  *
42852  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42853  * 
42854  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42855  * supported by this editor.</b><br/><br/>
42856  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42857  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42858  */
42859 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42860     /**
42861      * @cfg {Boolean} clearUp
42862      */
42863     clearUp : true,
42864       /**
42865      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42866      */
42867     toolbars : false,
42868    
42869      /**
42870      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42871      *                        Roo.resizable.
42872      */
42873     resizable : false,
42874      /**
42875      * @cfg {Number} height (in pixels)
42876      */   
42877     height: 300,
42878    /**
42879      * @cfg {Number} width (in pixels)
42880      */   
42881     width: 500,
42882     
42883     /**
42884      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42885      * 
42886      */
42887     stylesheets: false,
42888     
42889     
42890      /**
42891      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42892      * 
42893      */
42894     cblack: false,
42895     /**
42896      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42897      * 
42898      */
42899     cwhite: false,
42900     
42901      /**
42902      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42903      * 
42904      */
42905     black: false,
42906     /**
42907      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42908      * 
42909      */
42910     white: false,
42911     
42912     // id of frame..
42913     frameId: false,
42914     
42915     // private properties
42916     validationEvent : false,
42917     deferHeight: true,
42918     initialized : false,
42919     activated : false,
42920     
42921     onFocus : Roo.emptyFn,
42922     iframePad:3,
42923     hideMode:'offsets',
42924     
42925     actionMode : 'container', // defaults to hiding it...
42926     
42927     defaultAutoCreate : { // modified by initCompnoent..
42928         tag: "textarea",
42929         style:"width:500px;height:300px;",
42930         autocomplete: "new-password"
42931     },
42932
42933     // private
42934     initComponent : function(){
42935         this.addEvents({
42936             /**
42937              * @event initialize
42938              * Fires when the editor is fully initialized (including the iframe)
42939              * @param {HtmlEditor} this
42940              */
42941             initialize: true,
42942             /**
42943              * @event activate
42944              * Fires when the editor is first receives the focus. Any insertion must wait
42945              * until after this event.
42946              * @param {HtmlEditor} this
42947              */
42948             activate: true,
42949              /**
42950              * @event beforesync
42951              * Fires before the textarea is updated with content from the editor iframe. Return false
42952              * to cancel the sync.
42953              * @param {HtmlEditor} this
42954              * @param {String} html
42955              */
42956             beforesync: true,
42957              /**
42958              * @event beforepush
42959              * Fires before the iframe editor is updated with content from the textarea. Return false
42960              * to cancel the push.
42961              * @param {HtmlEditor} this
42962              * @param {String} html
42963              */
42964             beforepush: true,
42965              /**
42966              * @event sync
42967              * Fires when the textarea is updated with content from the editor iframe.
42968              * @param {HtmlEditor} this
42969              * @param {String} html
42970              */
42971             sync: true,
42972              /**
42973              * @event push
42974              * Fires when the iframe editor is updated with content from the textarea.
42975              * @param {HtmlEditor} this
42976              * @param {String} html
42977              */
42978             push: true,
42979              /**
42980              * @event editmodechange
42981              * Fires when the editor switches edit modes
42982              * @param {HtmlEditor} this
42983              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42984              */
42985             editmodechange: true,
42986             /**
42987              * @event editorevent
42988              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42989              * @param {HtmlEditor} this
42990              */
42991             editorevent: true,
42992             /**
42993              * @event firstfocus
42994              * Fires when on first focus - needed by toolbars..
42995              * @param {HtmlEditor} this
42996              */
42997             firstfocus: true,
42998             /**
42999              * @event autosave
43000              * Auto save the htmlEditor value as a file into Events
43001              * @param {HtmlEditor} this
43002              */
43003             autosave: true,
43004             /**
43005              * @event savedpreview
43006              * preview the saved version of htmlEditor
43007              * @param {HtmlEditor} this
43008              */
43009             savedpreview: true,
43010             
43011             /**
43012             * @event stylesheetsclick
43013             * Fires when press the Sytlesheets button
43014             * @param {Roo.HtmlEditorCore} this
43015             */
43016             stylesheetsclick: true
43017         });
43018         this.defaultAutoCreate =  {
43019             tag: "textarea",
43020             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
43021             autocomplete: "new-password"
43022         };
43023     },
43024
43025     /**
43026      * Protected method that will not generally be called directly. It
43027      * is called when the editor creates its toolbar. Override this method if you need to
43028      * add custom toolbar buttons.
43029      * @param {HtmlEditor} editor
43030      */
43031     createToolbar : function(editor){
43032         Roo.log("create toolbars");
43033         if (!editor.toolbars || !editor.toolbars.length) {
43034             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
43035         }
43036         
43037         for (var i =0 ; i < editor.toolbars.length;i++) {
43038             editor.toolbars[i] = Roo.factory(
43039                     typeof(editor.toolbars[i]) == 'string' ?
43040                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
43041                 Roo.form.HtmlEditor);
43042             editor.toolbars[i].init(editor);
43043         }
43044          
43045         
43046     },
43047
43048      
43049     // private
43050     onRender : function(ct, position)
43051     {
43052         var _t = this;
43053         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
43054         
43055         this.wrap = this.el.wrap({
43056             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
43057         });
43058         
43059         this.editorcore.onRender(ct, position);
43060          
43061         if (this.resizable) {
43062             this.resizeEl = new Roo.Resizable(this.wrap, {
43063                 pinned : true,
43064                 wrap: true,
43065                 dynamic : true,
43066                 minHeight : this.height,
43067                 height: this.height,
43068                 handles : this.resizable,
43069                 width: this.width,
43070                 listeners : {
43071                     resize : function(r, w, h) {
43072                         _t.onResize(w,h); // -something
43073                     }
43074                 }
43075             });
43076             
43077         }
43078         this.createToolbar(this);
43079        
43080         
43081         if(!this.width){
43082             this.setSize(this.wrap.getSize());
43083         }
43084         if (this.resizeEl) {
43085             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43086             // should trigger onReize..
43087         }
43088         
43089         this.keyNav = new Roo.KeyNav(this.el, {
43090             
43091             "tab" : function(e){
43092                 e.preventDefault();
43093                 
43094                 var value = this.getValue();
43095                 
43096                 var start = this.el.dom.selectionStart;
43097                 var end = this.el.dom.selectionEnd;
43098                 
43099                 if(!e.shiftKey){
43100                     
43101                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43102                     this.el.dom.setSelectionRange(end + 1, end + 1);
43103                     return;
43104                 }
43105                 
43106                 var f = value.substring(0, start).split("\t");
43107                 
43108                 if(f.pop().length != 0){
43109                     return;
43110                 }
43111                 
43112                 this.setValue(f.join("\t") + value.substring(end));
43113                 this.el.dom.setSelectionRange(start - 1, start - 1);
43114                 
43115             },
43116             
43117             "home" : function(e){
43118                 e.preventDefault();
43119                 
43120                 var curr = this.el.dom.selectionStart;
43121                 var lines = this.getValue().split("\n");
43122                 
43123                 if(!lines.length){
43124                     return;
43125                 }
43126                 
43127                 if(e.ctrlKey){
43128                     this.el.dom.setSelectionRange(0, 0);
43129                     return;
43130                 }
43131                 
43132                 var pos = 0;
43133                 
43134                 for (var i = 0; i < lines.length;i++) {
43135                     pos += lines[i].length;
43136                     
43137                     if(i != 0){
43138                         pos += 1;
43139                     }
43140                     
43141                     if(pos < curr){
43142                         continue;
43143                     }
43144                     
43145                     pos -= lines[i].length;
43146                     
43147                     break;
43148                 }
43149                 
43150                 if(!e.shiftKey){
43151                     this.el.dom.setSelectionRange(pos, pos);
43152                     return;
43153                 }
43154                 
43155                 this.el.dom.selectionStart = pos;
43156                 this.el.dom.selectionEnd = curr;
43157             },
43158             
43159             "end" : function(e){
43160                 e.preventDefault();
43161                 
43162                 var curr = this.el.dom.selectionStart;
43163                 var lines = this.getValue().split("\n");
43164                 
43165                 if(!lines.length){
43166                     return;
43167                 }
43168                 
43169                 if(e.ctrlKey){
43170                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43171                     return;
43172                 }
43173                 
43174                 var pos = 0;
43175                 
43176                 for (var i = 0; i < lines.length;i++) {
43177                     
43178                     pos += lines[i].length;
43179                     
43180                     if(i != 0){
43181                         pos += 1;
43182                     }
43183                     
43184                     if(pos < curr){
43185                         continue;
43186                     }
43187                     
43188                     break;
43189                 }
43190                 
43191                 if(!e.shiftKey){
43192                     this.el.dom.setSelectionRange(pos, pos);
43193                     return;
43194                 }
43195                 
43196                 this.el.dom.selectionStart = curr;
43197                 this.el.dom.selectionEnd = pos;
43198             },
43199
43200             scope : this,
43201
43202             doRelay : function(foo, bar, hname){
43203                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43204             },
43205
43206             forceKeyDown: true
43207         });
43208         
43209 //        if(this.autosave && this.w){
43210 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43211 //        }
43212     },
43213
43214     // private
43215     onResize : function(w, h)
43216     {
43217         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43218         var ew = false;
43219         var eh = false;
43220         
43221         if(this.el ){
43222             if(typeof w == 'number'){
43223                 var aw = w - this.wrap.getFrameWidth('lr');
43224                 this.el.setWidth(this.adjustWidth('textarea', aw));
43225                 ew = aw;
43226             }
43227             if(typeof h == 'number'){
43228                 var tbh = 0;
43229                 for (var i =0; i < this.toolbars.length;i++) {
43230                     // fixme - ask toolbars for heights?
43231                     tbh += this.toolbars[i].tb.el.getHeight();
43232                     if (this.toolbars[i].footer) {
43233                         tbh += this.toolbars[i].footer.el.getHeight();
43234                     }
43235                 }
43236                 
43237                 
43238                 
43239                 
43240                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43241                 ah -= 5; // knock a few pixes off for look..
43242 //                Roo.log(ah);
43243                 this.el.setHeight(this.adjustWidth('textarea', ah));
43244                 var eh = ah;
43245             }
43246         }
43247         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43248         this.editorcore.onResize(ew,eh);
43249         
43250     },
43251
43252     /**
43253      * Toggles the editor between standard and source edit mode.
43254      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43255      */
43256     toggleSourceEdit : function(sourceEditMode)
43257     {
43258         this.editorcore.toggleSourceEdit(sourceEditMode);
43259         
43260         if(this.editorcore.sourceEditMode){
43261             Roo.log('editor - showing textarea');
43262             
43263 //            Roo.log('in');
43264 //            Roo.log(this.syncValue());
43265             this.editorcore.syncValue();
43266             this.el.removeClass('x-hidden');
43267             this.el.dom.removeAttribute('tabIndex');
43268             this.el.focus();
43269             
43270             for (var i = 0; i < this.toolbars.length; i++) {
43271                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43272                     this.toolbars[i].tb.hide();
43273                     this.toolbars[i].footer.hide();
43274                 }
43275             }
43276             
43277         }else{
43278             Roo.log('editor - hiding textarea');
43279 //            Roo.log('out')
43280 //            Roo.log(this.pushValue()); 
43281             this.editorcore.pushValue();
43282             
43283             this.el.addClass('x-hidden');
43284             this.el.dom.setAttribute('tabIndex', -1);
43285             
43286             for (var i = 0; i < this.toolbars.length; i++) {
43287                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43288                     this.toolbars[i].tb.show();
43289                     this.toolbars[i].footer.show();
43290                 }
43291             }
43292             
43293             //this.deferFocus();
43294         }
43295         
43296         this.setSize(this.wrap.getSize());
43297         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43298         
43299         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43300     },
43301  
43302     // private (for BoxComponent)
43303     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43304
43305     // private (for BoxComponent)
43306     getResizeEl : function(){
43307         return this.wrap;
43308     },
43309
43310     // private (for BoxComponent)
43311     getPositionEl : function(){
43312         return this.wrap;
43313     },
43314
43315     // private
43316     initEvents : function(){
43317         this.originalValue = this.getValue();
43318     },
43319
43320     /**
43321      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43322      * @method
43323      */
43324     markInvalid : Roo.emptyFn,
43325     /**
43326      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43327      * @method
43328      */
43329     clearInvalid : Roo.emptyFn,
43330
43331     setValue : function(v){
43332         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43333         this.editorcore.pushValue();
43334     },
43335
43336      
43337     // private
43338     deferFocus : function(){
43339         this.focus.defer(10, this);
43340     },
43341
43342     // doc'ed in Field
43343     focus : function(){
43344         this.editorcore.focus();
43345         
43346     },
43347       
43348
43349     // private
43350     onDestroy : function(){
43351         
43352         
43353         
43354         if(this.rendered){
43355             
43356             for (var i =0; i < this.toolbars.length;i++) {
43357                 // fixme - ask toolbars for heights?
43358                 this.toolbars[i].onDestroy();
43359             }
43360             
43361             this.wrap.dom.innerHTML = '';
43362             this.wrap.remove();
43363         }
43364     },
43365
43366     // private
43367     onFirstFocus : function(){
43368         //Roo.log("onFirstFocus");
43369         this.editorcore.onFirstFocus();
43370          for (var i =0; i < this.toolbars.length;i++) {
43371             this.toolbars[i].onFirstFocus();
43372         }
43373         
43374     },
43375     
43376     // private
43377     syncValue : function()
43378     {
43379         this.editorcore.syncValue();
43380     },
43381     
43382     pushValue : function()
43383     {
43384         this.editorcore.pushValue();
43385     },
43386     
43387     setStylesheets : function(stylesheets)
43388     {
43389         this.editorcore.setStylesheets(stylesheets);
43390     },
43391     
43392     removeStylesheets : function()
43393     {
43394         this.editorcore.removeStylesheets();
43395     }
43396      
43397     
43398     // hide stuff that is not compatible
43399     /**
43400      * @event blur
43401      * @hide
43402      */
43403     /**
43404      * @event change
43405      * @hide
43406      */
43407     /**
43408      * @event focus
43409      * @hide
43410      */
43411     /**
43412      * @event specialkey
43413      * @hide
43414      */
43415     /**
43416      * @cfg {String} fieldClass @hide
43417      */
43418     /**
43419      * @cfg {String} focusClass @hide
43420      */
43421     /**
43422      * @cfg {String} autoCreate @hide
43423      */
43424     /**
43425      * @cfg {String} inputType @hide
43426      */
43427     /**
43428      * @cfg {String} invalidClass @hide
43429      */
43430     /**
43431      * @cfg {String} invalidText @hide
43432      */
43433     /**
43434      * @cfg {String} msgFx @hide
43435      */
43436     /**
43437      * @cfg {String} validateOnBlur @hide
43438      */
43439 });
43440  
43441     // <script type="text/javascript">
43442 /*
43443  * Based on
43444  * Ext JS Library 1.1.1
43445  * Copyright(c) 2006-2007, Ext JS, LLC.
43446  *  
43447  
43448  */
43449
43450 /**
43451  * @class Roo.form.HtmlEditorToolbar1
43452  * Basic Toolbar
43453  * 
43454  * Usage:
43455  *
43456  new Roo.form.HtmlEditor({
43457     ....
43458     toolbars : [
43459         new Roo.form.HtmlEditorToolbar1({
43460             disable : { fonts: 1 , format: 1, ..., ... , ...],
43461             btns : [ .... ]
43462         })
43463     }
43464      
43465  * 
43466  * @cfg {Object} disable List of elements to disable..
43467  * @cfg {Array} btns List of additional buttons.
43468  * 
43469  * 
43470  * NEEDS Extra CSS? 
43471  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43472  */
43473  
43474 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43475 {
43476     
43477     Roo.apply(this, config);
43478     
43479     // default disabled, based on 'good practice'..
43480     this.disable = this.disable || {};
43481     Roo.applyIf(this.disable, {
43482         fontSize : true,
43483         colors : true,
43484         specialElements : true
43485     });
43486     
43487     
43488     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43489     // dont call parent... till later.
43490 }
43491
43492 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43493     
43494     tb: false,
43495     
43496     rendered: false,
43497     
43498     editor : false,
43499     editorcore : false,
43500     /**
43501      * @cfg {Object} disable  List of toolbar elements to disable
43502          
43503      */
43504     disable : false,
43505     
43506     
43507      /**
43508      * @cfg {String} createLinkText The default text for the create link prompt
43509      */
43510     createLinkText : 'Please enter the URL for the link:',
43511     /**
43512      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43513      */
43514     defaultLinkValue : 'http:/'+'/',
43515    
43516     
43517       /**
43518      * @cfg {Array} fontFamilies An array of available font families
43519      */
43520     fontFamilies : [
43521         'Arial',
43522         'Courier New',
43523         'Tahoma',
43524         'Times New Roman',
43525         'Verdana'
43526     ],
43527     
43528     specialChars : [
43529            "&#169;",
43530           "&#174;",     
43531           "&#8482;",    
43532           "&#163;" ,    
43533          // "&#8212;",    
43534           "&#8230;",    
43535           "&#247;" ,    
43536         //  "&#225;" ,     ?? a acute?
43537            "&#8364;"    , //Euro
43538        //   "&#8220;"    ,
43539         //  "&#8221;"    ,
43540         //  "&#8226;"    ,
43541           "&#176;"  //   , // degrees
43542
43543          // "&#233;"     , // e ecute
43544          // "&#250;"     , // u ecute?
43545     ],
43546     
43547     specialElements : [
43548         {
43549             text: "Insert Table",
43550             xtype: 'MenuItem',
43551             xns : Roo.Menu,
43552             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43553                 
43554         },
43555         {    
43556             text: "Insert Image",
43557             xtype: 'MenuItem',
43558             xns : Roo.Menu,
43559             ihtml : '<img src="about:blank"/>'
43560             
43561         }
43562         
43563          
43564     ],
43565     
43566     
43567     inputElements : [ 
43568             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43569             "input:submit", "input:button", "select", "textarea", "label" ],
43570     formats : [
43571         ["p"] ,  
43572         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43573         ["pre"],[ "code"], 
43574         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43575         ['div'],['span']
43576     ],
43577     
43578     cleanStyles : [
43579         "font-size"
43580     ],
43581      /**
43582      * @cfg {String} defaultFont default font to use.
43583      */
43584     defaultFont: 'tahoma',
43585    
43586     fontSelect : false,
43587     
43588     
43589     formatCombo : false,
43590     
43591     init : function(editor)
43592     {
43593         this.editor = editor;
43594         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43595         var editorcore = this.editorcore;
43596         
43597         var _t = this;
43598         
43599         var fid = editorcore.frameId;
43600         var etb = this;
43601         function btn(id, toggle, handler){
43602             var xid = fid + '-'+ id ;
43603             return {
43604                 id : xid,
43605                 cmd : id,
43606                 cls : 'x-btn-icon x-edit-'+id,
43607                 enableToggle:toggle !== false,
43608                 scope: _t, // was editor...
43609                 handler:handler||_t.relayBtnCmd,
43610                 clickEvent:'mousedown',
43611                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43612                 tabIndex:-1
43613             };
43614         }
43615         
43616         
43617         
43618         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43619         this.tb = tb;
43620          // stop form submits
43621         tb.el.on('click', function(e){
43622             e.preventDefault(); // what does this do?
43623         });
43624
43625         if(!this.disable.font) { // && !Roo.isSafari){
43626             /* why no safari for fonts 
43627             editor.fontSelect = tb.el.createChild({
43628                 tag:'select',
43629                 tabIndex: -1,
43630                 cls:'x-font-select',
43631                 html: this.createFontOptions()
43632             });
43633             
43634             editor.fontSelect.on('change', function(){
43635                 var font = editor.fontSelect.dom.value;
43636                 editor.relayCmd('fontname', font);
43637                 editor.deferFocus();
43638             }, editor);
43639             
43640             tb.add(
43641                 editor.fontSelect.dom,
43642                 '-'
43643             );
43644             */
43645             
43646         };
43647         if(!this.disable.formats){
43648             this.formatCombo = new Roo.form.ComboBox({
43649                 store: new Roo.data.SimpleStore({
43650                     id : 'tag',
43651                     fields: ['tag'],
43652                     data : this.formats // from states.js
43653                 }),
43654                 blockFocus : true,
43655                 name : '',
43656                 //autoCreate : {tag: "div",  size: "20"},
43657                 displayField:'tag',
43658                 typeAhead: false,
43659                 mode: 'local',
43660                 editable : false,
43661                 triggerAction: 'all',
43662                 emptyText:'Add tag',
43663                 selectOnFocus:true,
43664                 width:135,
43665                 listeners : {
43666                     'select': function(c, r, i) {
43667                         editorcore.insertTag(r.get('tag'));
43668                         editor.focus();
43669                     }
43670                 }
43671
43672             });
43673             tb.addField(this.formatCombo);
43674             
43675         }
43676         
43677         if(!this.disable.format){
43678             tb.add(
43679                 btn('bold'),
43680                 btn('italic'),
43681                 btn('underline'),
43682                 btn('strikethrough')
43683             );
43684         };
43685         if(!this.disable.fontSize){
43686             tb.add(
43687                 '-',
43688                 
43689                 
43690                 btn('increasefontsize', false, editorcore.adjustFont),
43691                 btn('decreasefontsize', false, editorcore.adjustFont)
43692             );
43693         };
43694         
43695         
43696         if(!this.disable.colors){
43697             tb.add(
43698                 '-', {
43699                     id:editorcore.frameId +'-forecolor',
43700                     cls:'x-btn-icon x-edit-forecolor',
43701                     clickEvent:'mousedown',
43702                     tooltip: this.buttonTips['forecolor'] || undefined,
43703                     tabIndex:-1,
43704                     menu : new Roo.menu.ColorMenu({
43705                         allowReselect: true,
43706                         focus: Roo.emptyFn,
43707                         value:'000000',
43708                         plain:true,
43709                         selectHandler: function(cp, color){
43710                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43711                             editor.deferFocus();
43712                         },
43713                         scope: editorcore,
43714                         clickEvent:'mousedown'
43715                     })
43716                 }, {
43717                     id:editorcore.frameId +'backcolor',
43718                     cls:'x-btn-icon x-edit-backcolor',
43719                     clickEvent:'mousedown',
43720                     tooltip: this.buttonTips['backcolor'] || undefined,
43721                     tabIndex:-1,
43722                     menu : new Roo.menu.ColorMenu({
43723                         focus: Roo.emptyFn,
43724                         value:'FFFFFF',
43725                         plain:true,
43726                         allowReselect: true,
43727                         selectHandler: function(cp, color){
43728                             if(Roo.isGecko){
43729                                 editorcore.execCmd('useCSS', false);
43730                                 editorcore.execCmd('hilitecolor', color);
43731                                 editorcore.execCmd('useCSS', true);
43732                                 editor.deferFocus();
43733                             }else{
43734                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43735                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43736                                 editor.deferFocus();
43737                             }
43738                         },
43739                         scope:editorcore,
43740                         clickEvent:'mousedown'
43741                     })
43742                 }
43743             );
43744         };
43745         // now add all the items...
43746         
43747
43748         if(!this.disable.alignments){
43749             tb.add(
43750                 '-',
43751                 btn('justifyleft'),
43752                 btn('justifycenter'),
43753                 btn('justifyright')
43754             );
43755         };
43756
43757         //if(!Roo.isSafari){
43758             if(!this.disable.links){
43759                 tb.add(
43760                     '-',
43761                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43762                 );
43763             };
43764
43765             if(!this.disable.lists){
43766                 tb.add(
43767                     '-',
43768                     btn('insertorderedlist'),
43769                     btn('insertunorderedlist')
43770                 );
43771             }
43772             if(!this.disable.sourceEdit){
43773                 tb.add(
43774                     '-',
43775                     btn('sourceedit', true, function(btn){
43776                         this.toggleSourceEdit(btn.pressed);
43777                     })
43778                 );
43779             }
43780         //}
43781         
43782         var smenu = { };
43783         // special menu.. - needs to be tidied up..
43784         if (!this.disable.special) {
43785             smenu = {
43786                 text: "&#169;",
43787                 cls: 'x-edit-none',
43788                 
43789                 menu : {
43790                     items : []
43791                 }
43792             };
43793             for (var i =0; i < this.specialChars.length; i++) {
43794                 smenu.menu.items.push({
43795                     
43796                     html: this.specialChars[i],
43797                     handler: function(a,b) {
43798                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43799                         //editor.insertAtCursor(a.html);
43800                         
43801                     },
43802                     tabIndex:-1
43803                 });
43804             }
43805             
43806             
43807             tb.add(smenu);
43808             
43809             
43810         }
43811         
43812         var cmenu = { };
43813         if (!this.disable.cleanStyles) {
43814             cmenu = {
43815                 cls: 'x-btn-icon x-btn-clear',
43816                 
43817                 menu : {
43818                     items : []
43819                 }
43820             };
43821             for (var i =0; i < this.cleanStyles.length; i++) {
43822                 cmenu.menu.items.push({
43823                     actiontype : this.cleanStyles[i],
43824                     html: 'Remove ' + this.cleanStyles[i],
43825                     handler: function(a,b) {
43826 //                        Roo.log(a);
43827 //                        Roo.log(b);
43828                         var c = Roo.get(editorcore.doc.body);
43829                         c.select('[style]').each(function(s) {
43830                             s.dom.style.removeProperty(a.actiontype);
43831                         });
43832                         editorcore.syncValue();
43833                     },
43834                     tabIndex:-1
43835                 });
43836             }
43837              cmenu.menu.items.push({
43838                 actiontype : 'tablewidths',
43839                 html: 'Remove Table Widths',
43840                 handler: function(a,b) {
43841                     editorcore.cleanTableWidths();
43842                     editorcore.syncValue();
43843                 },
43844                 tabIndex:-1
43845             });
43846             cmenu.menu.items.push({
43847                 actiontype : 'word',
43848                 html: 'Remove MS Word Formating',
43849                 handler: function(a,b) {
43850                     editorcore.cleanWord();
43851                     editorcore.syncValue();
43852                 },
43853                 tabIndex:-1
43854             });
43855             
43856             cmenu.menu.items.push({
43857                 actiontype : 'all',
43858                 html: 'Remove All Styles',
43859                 handler: function(a,b) {
43860                     
43861                     var c = Roo.get(editorcore.doc.body);
43862                     c.select('[style]').each(function(s) {
43863                         s.dom.removeAttribute('style');
43864                     });
43865                     editorcore.syncValue();
43866                 },
43867                 tabIndex:-1
43868             });
43869             
43870             cmenu.menu.items.push({
43871                 actiontype : 'all',
43872                 html: 'Remove All CSS Classes',
43873                 handler: function(a,b) {
43874                     
43875                     var c = Roo.get(editorcore.doc.body);
43876                     c.select('[class]').each(function(s) {
43877                         s.dom.className = '';
43878                     });
43879                     editorcore.syncValue();
43880                 },
43881                 tabIndex:-1
43882             });
43883             
43884              cmenu.menu.items.push({
43885                 actiontype : 'tidy',
43886                 html: 'Tidy HTML Source',
43887                 handler: function(a,b) {
43888                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43889                     editorcore.syncValue();
43890                 },
43891                 tabIndex:-1
43892             });
43893             
43894             
43895             tb.add(cmenu);
43896         }
43897          
43898         if (!this.disable.specialElements) {
43899             var semenu = {
43900                 text: "Other;",
43901                 cls: 'x-edit-none',
43902                 menu : {
43903                     items : []
43904                 }
43905             };
43906             for (var i =0; i < this.specialElements.length; i++) {
43907                 semenu.menu.items.push(
43908                     Roo.apply({ 
43909                         handler: function(a,b) {
43910                             editor.insertAtCursor(this.ihtml);
43911                         }
43912                     }, this.specialElements[i])
43913                 );
43914                     
43915             }
43916             
43917             tb.add(semenu);
43918             
43919             
43920         }
43921          
43922         
43923         if (this.btns) {
43924             for(var i =0; i< this.btns.length;i++) {
43925                 var b = Roo.factory(this.btns[i],Roo.form);
43926                 b.cls =  'x-edit-none';
43927                 
43928                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43929                     b.cls += ' x-init-enable';
43930                 }
43931                 
43932                 b.scope = editorcore;
43933                 tb.add(b);
43934             }
43935         
43936         }
43937         
43938         
43939         
43940         // disable everything...
43941         
43942         this.tb.items.each(function(item){
43943             
43944            if(
43945                 item.id != editorcore.frameId+ '-sourceedit' && 
43946                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43947             ){
43948                 
43949                 item.disable();
43950             }
43951         });
43952         this.rendered = true;
43953         
43954         // the all the btns;
43955         editor.on('editorevent', this.updateToolbar, this);
43956         // other toolbars need to implement this..
43957         //editor.on('editmodechange', this.updateToolbar, this);
43958     },
43959     
43960     
43961     relayBtnCmd : function(btn) {
43962         this.editorcore.relayCmd(btn.cmd);
43963     },
43964     // private used internally
43965     createLink : function(){
43966         Roo.log("create link?");
43967         var url = prompt(this.createLinkText, this.defaultLinkValue);
43968         if(url && url != 'http:/'+'/'){
43969             this.editorcore.relayCmd('createlink', url);
43970         }
43971     },
43972
43973     
43974     /**
43975      * Protected method that will not generally be called directly. It triggers
43976      * a toolbar update by reading the markup state of the current selection in the editor.
43977      */
43978     updateToolbar: function(){
43979
43980         if(!this.editorcore.activated){
43981             this.editor.onFirstFocus();
43982             return;
43983         }
43984
43985         var btns = this.tb.items.map, 
43986             doc = this.editorcore.doc,
43987             frameId = this.editorcore.frameId;
43988
43989         if(!this.disable.font && !Roo.isSafari){
43990             /*
43991             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43992             if(name != this.fontSelect.dom.value){
43993                 this.fontSelect.dom.value = name;
43994             }
43995             */
43996         }
43997         if(!this.disable.format){
43998             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43999             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
44000             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
44001             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
44002         }
44003         if(!this.disable.alignments){
44004             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
44005             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
44006             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
44007         }
44008         if(!Roo.isSafari && !this.disable.lists){
44009             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
44010             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
44011         }
44012         
44013         var ans = this.editorcore.getAllAncestors();
44014         if (this.formatCombo) {
44015             
44016             
44017             var store = this.formatCombo.store;
44018             this.formatCombo.setValue("");
44019             for (var i =0; i < ans.length;i++) {
44020                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
44021                     // select it..
44022                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
44023                     break;
44024                 }
44025             }
44026         }
44027         
44028         
44029         
44030         // hides menus... - so this cant be on a menu...
44031         Roo.menu.MenuMgr.hideAll();
44032
44033         //this.editorsyncValue();
44034     },
44035    
44036     
44037     createFontOptions : function(){
44038         var buf = [], fs = this.fontFamilies, ff, lc;
44039         
44040         
44041         
44042         for(var i = 0, len = fs.length; i< len; i++){
44043             ff = fs[i];
44044             lc = ff.toLowerCase();
44045             buf.push(
44046                 '<option value="',lc,'" style="font-family:',ff,';"',
44047                     (this.defaultFont == lc ? ' selected="true">' : '>'),
44048                     ff,
44049                 '</option>'
44050             );
44051         }
44052         return buf.join('');
44053     },
44054     
44055     toggleSourceEdit : function(sourceEditMode){
44056         
44057         Roo.log("toolbar toogle");
44058         if(sourceEditMode === undefined){
44059             sourceEditMode = !this.sourceEditMode;
44060         }
44061         this.sourceEditMode = sourceEditMode === true;
44062         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
44063         // just toggle the button?
44064         if(btn.pressed !== this.sourceEditMode){
44065             btn.toggle(this.sourceEditMode);
44066             return;
44067         }
44068         
44069         if(sourceEditMode){
44070             Roo.log("disabling buttons");
44071             this.tb.items.each(function(item){
44072                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
44073                     item.disable();
44074                 }
44075             });
44076           
44077         }else{
44078             Roo.log("enabling buttons");
44079             if(this.editorcore.initialized){
44080                 this.tb.items.each(function(item){
44081                     item.enable();
44082                 });
44083             }
44084             
44085         }
44086         Roo.log("calling toggole on editor");
44087         // tell the editor that it's been pressed..
44088         this.editor.toggleSourceEdit(sourceEditMode);
44089        
44090     },
44091      /**
44092      * Object collection of toolbar tooltips for the buttons in the editor. The key
44093      * is the command id associated with that button and the value is a valid QuickTips object.
44094      * For example:
44095 <pre><code>
44096 {
44097     bold : {
44098         title: 'Bold (Ctrl+B)',
44099         text: 'Make the selected text bold.',
44100         cls: 'x-html-editor-tip'
44101     },
44102     italic : {
44103         title: 'Italic (Ctrl+I)',
44104         text: 'Make the selected text italic.',
44105         cls: 'x-html-editor-tip'
44106     },
44107     ...
44108 </code></pre>
44109     * @type Object
44110      */
44111     buttonTips : {
44112         bold : {
44113             title: 'Bold (Ctrl+B)',
44114             text: 'Make the selected text bold.',
44115             cls: 'x-html-editor-tip'
44116         },
44117         italic : {
44118             title: 'Italic (Ctrl+I)',
44119             text: 'Make the selected text italic.',
44120             cls: 'x-html-editor-tip'
44121         },
44122         underline : {
44123             title: 'Underline (Ctrl+U)',
44124             text: 'Underline the selected text.',
44125             cls: 'x-html-editor-tip'
44126         },
44127         strikethrough : {
44128             title: 'Strikethrough',
44129             text: 'Strikethrough the selected text.',
44130             cls: 'x-html-editor-tip'
44131         },
44132         increasefontsize : {
44133             title: 'Grow Text',
44134             text: 'Increase the font size.',
44135             cls: 'x-html-editor-tip'
44136         },
44137         decreasefontsize : {
44138             title: 'Shrink Text',
44139             text: 'Decrease the font size.',
44140             cls: 'x-html-editor-tip'
44141         },
44142         backcolor : {
44143             title: 'Text Highlight Color',
44144             text: 'Change the background color of the selected text.',
44145             cls: 'x-html-editor-tip'
44146         },
44147         forecolor : {
44148             title: 'Font Color',
44149             text: 'Change the color of the selected text.',
44150             cls: 'x-html-editor-tip'
44151         },
44152         justifyleft : {
44153             title: 'Align Text Left',
44154             text: 'Align text to the left.',
44155             cls: 'x-html-editor-tip'
44156         },
44157         justifycenter : {
44158             title: 'Center Text',
44159             text: 'Center text in the editor.',
44160             cls: 'x-html-editor-tip'
44161         },
44162         justifyright : {
44163             title: 'Align Text Right',
44164             text: 'Align text to the right.',
44165             cls: 'x-html-editor-tip'
44166         },
44167         insertunorderedlist : {
44168             title: 'Bullet List',
44169             text: 'Start a bulleted list.',
44170             cls: 'x-html-editor-tip'
44171         },
44172         insertorderedlist : {
44173             title: 'Numbered List',
44174             text: 'Start a numbered list.',
44175             cls: 'x-html-editor-tip'
44176         },
44177         createlink : {
44178             title: 'Hyperlink',
44179             text: 'Make the selected text a hyperlink.',
44180             cls: 'x-html-editor-tip'
44181         },
44182         sourceedit : {
44183             title: 'Source Edit',
44184             text: 'Switch to source editing mode.',
44185             cls: 'x-html-editor-tip'
44186         }
44187     },
44188     // private
44189     onDestroy : function(){
44190         if(this.rendered){
44191             
44192             this.tb.items.each(function(item){
44193                 if(item.menu){
44194                     item.menu.removeAll();
44195                     if(item.menu.el){
44196                         item.menu.el.destroy();
44197                     }
44198                 }
44199                 item.destroy();
44200             });
44201              
44202         }
44203     },
44204     onFirstFocus: function() {
44205         this.tb.items.each(function(item){
44206            item.enable();
44207         });
44208     }
44209 });
44210
44211
44212
44213
44214 // <script type="text/javascript">
44215 /*
44216  * Based on
44217  * Ext JS Library 1.1.1
44218  * Copyright(c) 2006-2007, Ext JS, LLC.
44219  *  
44220  
44221  */
44222
44223  
44224 /**
44225  * @class Roo.form.HtmlEditor.ToolbarContext
44226  * Context Toolbar
44227  * 
44228  * Usage:
44229  *
44230  new Roo.form.HtmlEditor({
44231     ....
44232     toolbars : [
44233         { xtype: 'ToolbarStandard', styles : {} }
44234         { xtype: 'ToolbarContext', disable : {} }
44235     ]
44236 })
44237
44238      
44239  * 
44240  * @config : {Object} disable List of elements to disable.. (not done yet.)
44241  * @config : {Object} styles  Map of styles available.
44242  * 
44243  */
44244
44245 Roo.form.HtmlEditor.ToolbarContext = function(config)
44246 {
44247     
44248     Roo.apply(this, config);
44249     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44250     // dont call parent... till later.
44251     this.styles = this.styles || {};
44252 }
44253
44254  
44255
44256 Roo.form.HtmlEditor.ToolbarContext.types = {
44257     'IMG' : {
44258         width : {
44259             title: "Width",
44260             width: 40
44261         },
44262         height:  {
44263             title: "Height",
44264             width: 40
44265         },
44266         align: {
44267             title: "Align",
44268             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44269             width : 80
44270             
44271         },
44272         border: {
44273             title: "Border",
44274             width: 40
44275         },
44276         alt: {
44277             title: "Alt",
44278             width: 120
44279         },
44280         src : {
44281             title: "Src",
44282             width: 220
44283         }
44284         
44285     },
44286     'A' : {
44287         name : {
44288             title: "Name",
44289             width: 50
44290         },
44291         target:  {
44292             title: "Target",
44293             width: 120
44294         },
44295         href:  {
44296             title: "Href",
44297             width: 220
44298         } // border?
44299         
44300     },
44301     'TABLE' : {
44302         rows : {
44303             title: "Rows",
44304             width: 20
44305         },
44306         cols : {
44307             title: "Cols",
44308             width: 20
44309         },
44310         width : {
44311             title: "Width",
44312             width: 40
44313         },
44314         height : {
44315             title: "Height",
44316             width: 40
44317         },
44318         border : {
44319             title: "Border",
44320             width: 20
44321         }
44322     },
44323     'TD' : {
44324         width : {
44325             title: "Width",
44326             width: 40
44327         },
44328         height : {
44329             title: "Height",
44330             width: 40
44331         },   
44332         align: {
44333             title: "Align",
44334             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44335             width: 80
44336         },
44337         valign: {
44338             title: "Valign",
44339             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44340             width: 80
44341         },
44342         colspan: {
44343             title: "Colspan",
44344             width: 20
44345             
44346         },
44347          'font-family'  : {
44348             title : "Font",
44349             style : 'fontFamily',
44350             displayField: 'display',
44351             optname : 'font-family',
44352             width: 140
44353         }
44354     },
44355     'INPUT' : {
44356         name : {
44357             title: "name",
44358             width: 120
44359         },
44360         value : {
44361             title: "Value",
44362             width: 120
44363         },
44364         width : {
44365             title: "Width",
44366             width: 40
44367         }
44368     },
44369     'LABEL' : {
44370         'for' : {
44371             title: "For",
44372             width: 120
44373         }
44374     },
44375     'TEXTAREA' : {
44376           name : {
44377             title: "name",
44378             width: 120
44379         },
44380         rows : {
44381             title: "Rows",
44382             width: 20
44383         },
44384         cols : {
44385             title: "Cols",
44386             width: 20
44387         }
44388     },
44389     'SELECT' : {
44390         name : {
44391             title: "name",
44392             width: 120
44393         },
44394         selectoptions : {
44395             title: "Options",
44396             width: 200
44397         }
44398     },
44399     
44400     // should we really allow this??
44401     // should this just be 
44402     'BODY' : {
44403         title : {
44404             title: "Title",
44405             width: 200,
44406             disabled : true
44407         }
44408     },
44409     'SPAN' : {
44410         'font-family'  : {
44411             title : "Font",
44412             style : 'fontFamily',
44413             displayField: 'display',
44414             optname : 'font-family',
44415             width: 140
44416         }
44417     },
44418     'DIV' : {
44419         'font-family'  : {
44420             title : "Font",
44421             style : 'fontFamily',
44422             displayField: 'display',
44423             optname : 'font-family',
44424             width: 140
44425         }
44426     },
44427      'P' : {
44428         'font-family'  : {
44429             title : "Font",
44430             style : 'fontFamily',
44431             displayField: 'display',
44432             optname : 'font-family',
44433             width: 140
44434         }
44435     },
44436     
44437     '*' : {
44438         // empty..
44439     }
44440
44441 };
44442
44443 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44444 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44445
44446 Roo.form.HtmlEditor.ToolbarContext.options = {
44447         'font-family'  : [ 
44448                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44449                 [ 'Courier New', 'Courier New'],
44450                 [ 'Tahoma', 'Tahoma'],
44451                 [ 'Times New Roman,serif', 'Times'],
44452                 [ 'Verdana','Verdana' ]
44453         ]
44454 };
44455
44456 // fixme - these need to be configurable..
44457  
44458
44459 //Roo.form.HtmlEditor.ToolbarContext.types
44460
44461
44462 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44463     
44464     tb: false,
44465     
44466     rendered: false,
44467     
44468     editor : false,
44469     editorcore : false,
44470     /**
44471      * @cfg {Object} disable  List of toolbar elements to disable
44472          
44473      */
44474     disable : false,
44475     /**
44476      * @cfg {Object} styles List of styles 
44477      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44478      *
44479      * These must be defined in the page, so they get rendered correctly..
44480      * .headline { }
44481      * TD.underline { }
44482      * 
44483      */
44484     styles : false,
44485     
44486     options: false,
44487     
44488     toolbars : false,
44489     
44490     init : function(editor)
44491     {
44492         this.editor = editor;
44493         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44494         var editorcore = this.editorcore;
44495         
44496         var fid = editorcore.frameId;
44497         var etb = this;
44498         function btn(id, toggle, handler){
44499             var xid = fid + '-'+ id ;
44500             return {
44501                 id : xid,
44502                 cmd : id,
44503                 cls : 'x-btn-icon x-edit-'+id,
44504                 enableToggle:toggle !== false,
44505                 scope: editorcore, // was editor...
44506                 handler:handler||editorcore.relayBtnCmd,
44507                 clickEvent:'mousedown',
44508                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44509                 tabIndex:-1
44510             };
44511         }
44512         // create a new element.
44513         var wdiv = editor.wrap.createChild({
44514                 tag: 'div'
44515             }, editor.wrap.dom.firstChild.nextSibling, true);
44516         
44517         // can we do this more than once??
44518         
44519          // stop form submits
44520       
44521  
44522         // disable everything...
44523         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44524         this.toolbars = {};
44525            
44526         for (var i in  ty) {
44527           
44528             this.toolbars[i] = this.buildToolbar(ty[i],i);
44529         }
44530         this.tb = this.toolbars.BODY;
44531         this.tb.el.show();
44532         this.buildFooter();
44533         this.footer.show();
44534         editor.on('hide', function( ) { this.footer.hide() }, this);
44535         editor.on('show', function( ) { this.footer.show() }, this);
44536         
44537          
44538         this.rendered = true;
44539         
44540         // the all the btns;
44541         editor.on('editorevent', this.updateToolbar, this);
44542         // other toolbars need to implement this..
44543         //editor.on('editmodechange', this.updateToolbar, this);
44544     },
44545     
44546     
44547     
44548     /**
44549      * Protected method that will not generally be called directly. It triggers
44550      * a toolbar update by reading the markup state of the current selection in the editor.
44551      *
44552      * Note you can force an update by calling on('editorevent', scope, false)
44553      */
44554     updateToolbar: function(editor,ev,sel){
44555
44556         //Roo.log(ev);
44557         // capture mouse up - this is handy for selecting images..
44558         // perhaps should go somewhere else...
44559         if(!this.editorcore.activated){
44560              this.editor.onFirstFocus();
44561             return;
44562         }
44563         
44564         
44565         
44566         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44567         // selectNode - might want to handle IE?
44568         if (ev &&
44569             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44570             ev.target && ev.target.tagName == 'IMG') {
44571             // they have click on an image...
44572             // let's see if we can change the selection...
44573             sel = ev.target;
44574          
44575               var nodeRange = sel.ownerDocument.createRange();
44576             try {
44577                 nodeRange.selectNode(sel);
44578             } catch (e) {
44579                 nodeRange.selectNodeContents(sel);
44580             }
44581             //nodeRange.collapse(true);
44582             var s = this.editorcore.win.getSelection();
44583             s.removeAllRanges();
44584             s.addRange(nodeRange);
44585         }  
44586         
44587       
44588         var updateFooter = sel ? false : true;
44589         
44590         
44591         var ans = this.editorcore.getAllAncestors();
44592         
44593         // pick
44594         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44595         
44596         if (!sel) { 
44597             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44598             sel = sel ? sel : this.editorcore.doc.body;
44599             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44600             
44601         }
44602         // pick a menu that exists..
44603         var tn = sel.tagName.toUpperCase();
44604         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44605         
44606         tn = sel.tagName.toUpperCase();
44607         
44608         var lastSel = this.tb.selectedNode;
44609         
44610         this.tb.selectedNode = sel;
44611         
44612         // if current menu does not match..
44613         
44614         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44615                 
44616             this.tb.el.hide();
44617             ///console.log("show: " + tn);
44618             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44619             this.tb.el.show();
44620             // update name
44621             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44622             
44623             
44624             // update attributes
44625             if (this.tb.fields) {
44626                 this.tb.fields.each(function(e) {
44627                     if (e.stylename) {
44628                         e.setValue(sel.style[e.stylename]);
44629                         return;
44630                     } 
44631                    e.setValue(sel.getAttribute(e.attrname));
44632                 });
44633             }
44634             
44635             var hasStyles = false;
44636             for(var i in this.styles) {
44637                 hasStyles = true;
44638                 break;
44639             }
44640             
44641             // update styles
44642             if (hasStyles) { 
44643                 var st = this.tb.fields.item(0);
44644                 
44645                 st.store.removeAll();
44646                
44647                 
44648                 var cn = sel.className.split(/\s+/);
44649                 
44650                 var avs = [];
44651                 if (this.styles['*']) {
44652                     
44653                     Roo.each(this.styles['*'], function(v) {
44654                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44655                     });
44656                 }
44657                 if (this.styles[tn]) { 
44658                     Roo.each(this.styles[tn], function(v) {
44659                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44660                     });
44661                 }
44662                 
44663                 st.store.loadData(avs);
44664                 st.collapse();
44665                 st.setValue(cn);
44666             }
44667             // flag our selected Node.
44668             this.tb.selectedNode = sel;
44669            
44670            
44671             Roo.menu.MenuMgr.hideAll();
44672
44673         }
44674         
44675         if (!updateFooter) {
44676             //this.footDisp.dom.innerHTML = ''; 
44677             return;
44678         }
44679         // update the footer
44680         //
44681         var html = '';
44682         
44683         this.footerEls = ans.reverse();
44684         Roo.each(this.footerEls, function(a,i) {
44685             if (!a) { return; }
44686             html += html.length ? ' &gt; '  :  '';
44687             
44688             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44689             
44690         });
44691        
44692         // 
44693         var sz = this.footDisp.up('td').getSize();
44694         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44695         this.footDisp.dom.style.marginLeft = '5px';
44696         
44697         this.footDisp.dom.style.overflow = 'hidden';
44698         
44699         this.footDisp.dom.innerHTML = html;
44700             
44701         //this.editorsyncValue();
44702     },
44703      
44704     
44705    
44706        
44707     // private
44708     onDestroy : function(){
44709         if(this.rendered){
44710             
44711             this.tb.items.each(function(item){
44712                 if(item.menu){
44713                     item.menu.removeAll();
44714                     if(item.menu.el){
44715                         item.menu.el.destroy();
44716                     }
44717                 }
44718                 item.destroy();
44719             });
44720              
44721         }
44722     },
44723     onFirstFocus: function() {
44724         // need to do this for all the toolbars..
44725         this.tb.items.each(function(item){
44726            item.enable();
44727         });
44728     },
44729     buildToolbar: function(tlist, nm)
44730     {
44731         var editor = this.editor;
44732         var editorcore = this.editorcore;
44733          // create a new element.
44734         var wdiv = editor.wrap.createChild({
44735                 tag: 'div'
44736             }, editor.wrap.dom.firstChild.nextSibling, true);
44737         
44738        
44739         var tb = new Roo.Toolbar(wdiv);
44740         // add the name..
44741         
44742         tb.add(nm+ ":&nbsp;");
44743         
44744         var styles = [];
44745         for(var i in this.styles) {
44746             styles.push(i);
44747         }
44748         
44749         // styles...
44750         if (styles && styles.length) {
44751             
44752             // this needs a multi-select checkbox...
44753             tb.addField( new Roo.form.ComboBox({
44754                 store: new Roo.data.SimpleStore({
44755                     id : 'val',
44756                     fields: ['val', 'selected'],
44757                     data : [] 
44758                 }),
44759                 name : '-roo-edit-className',
44760                 attrname : 'className',
44761                 displayField: 'val',
44762                 typeAhead: false,
44763                 mode: 'local',
44764                 editable : false,
44765                 triggerAction: 'all',
44766                 emptyText:'Select Style',
44767                 selectOnFocus:true,
44768                 width: 130,
44769                 listeners : {
44770                     'select': function(c, r, i) {
44771                         // initial support only for on class per el..
44772                         tb.selectedNode.className =  r ? r.get('val') : '';
44773                         editorcore.syncValue();
44774                     }
44775                 }
44776     
44777             }));
44778         }
44779         
44780         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44781         var tbops = tbc.options;
44782         
44783         for (var i in tlist) {
44784             
44785             var item = tlist[i];
44786             tb.add(item.title + ":&nbsp;");
44787             
44788             
44789             //optname == used so you can configure the options available..
44790             var opts = item.opts ? item.opts : false;
44791             if (item.optname) {
44792                 opts = tbops[item.optname];
44793            
44794             }
44795             
44796             if (opts) {
44797                 // opts == pulldown..
44798                 tb.addField( new Roo.form.ComboBox({
44799                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44800                         id : 'val',
44801                         fields: ['val', 'display'],
44802                         data : opts  
44803                     }),
44804                     name : '-roo-edit-' + i,
44805                     attrname : i,
44806                     stylename : item.style ? item.style : false,
44807                     displayField: item.displayField ? item.displayField : 'val',
44808                     valueField :  'val',
44809                     typeAhead: false,
44810                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44811                     editable : false,
44812                     triggerAction: 'all',
44813                     emptyText:'Select',
44814                     selectOnFocus:true,
44815                     width: item.width ? item.width  : 130,
44816                     listeners : {
44817                         'select': function(c, r, i) {
44818                             if (c.stylename) {
44819                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44820                                 return;
44821                             }
44822                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44823                         }
44824                     }
44825
44826                 }));
44827                 continue;
44828                     
44829                  
44830                 
44831                 tb.addField( new Roo.form.TextField({
44832                     name: i,
44833                     width: 100,
44834                     //allowBlank:false,
44835                     value: ''
44836                 }));
44837                 continue;
44838             }
44839             tb.addField( new Roo.form.TextField({
44840                 name: '-roo-edit-' + i,
44841                 attrname : i,
44842                 
44843                 width: item.width,
44844                 //allowBlank:true,
44845                 value: '',
44846                 listeners: {
44847                     'change' : function(f, nv, ov) {
44848                         tb.selectedNode.setAttribute(f.attrname, nv);
44849                     }
44850                 }
44851             }));
44852              
44853         }
44854         
44855         var _this = this;
44856         
44857         if(nm == 'BODY'){
44858             tb.addSeparator();
44859         
44860             tb.addButton( {
44861                 text: 'Stylesheets',
44862
44863                 listeners : {
44864                     click : function ()
44865                     {
44866                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44867                     }
44868                 }
44869             });
44870         }
44871         
44872         tb.addFill();
44873         tb.addButton( {
44874             text: 'Remove Tag',
44875     
44876             listeners : {
44877                 click : function ()
44878                 {
44879                     // remove
44880                     // undo does not work.
44881                      
44882                     var sn = tb.selectedNode;
44883                     
44884                     var pn = sn.parentNode;
44885                     
44886                     var stn =  sn.childNodes[0];
44887                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44888                     while (sn.childNodes.length) {
44889                         var node = sn.childNodes[0];
44890                         sn.removeChild(node);
44891                         //Roo.log(node);
44892                         pn.insertBefore(node, sn);
44893                         
44894                     }
44895                     pn.removeChild(sn);
44896                     var range = editorcore.createRange();
44897         
44898                     range.setStart(stn,0);
44899                     range.setEnd(en,0); //????
44900                     //range.selectNode(sel);
44901                     
44902                     
44903                     var selection = editorcore.getSelection();
44904                     selection.removeAllRanges();
44905                     selection.addRange(range);
44906                     
44907                     
44908                     
44909                     //_this.updateToolbar(null, null, pn);
44910                     _this.updateToolbar(null, null, null);
44911                     _this.footDisp.dom.innerHTML = ''; 
44912                 }
44913             }
44914             
44915                     
44916                 
44917             
44918         });
44919         
44920         
44921         tb.el.on('click', function(e){
44922             e.preventDefault(); // what does this do?
44923         });
44924         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44925         tb.el.hide();
44926         tb.name = nm;
44927         // dont need to disable them... as they will get hidden
44928         return tb;
44929          
44930         
44931     },
44932     buildFooter : function()
44933     {
44934         
44935         var fel = this.editor.wrap.createChild();
44936         this.footer = new Roo.Toolbar(fel);
44937         // toolbar has scrolly on left / right?
44938         var footDisp= new Roo.Toolbar.Fill();
44939         var _t = this;
44940         this.footer.add(
44941             {
44942                 text : '&lt;',
44943                 xtype: 'Button',
44944                 handler : function() {
44945                     _t.footDisp.scrollTo('left',0,true)
44946                 }
44947             }
44948         );
44949         this.footer.add( footDisp );
44950         this.footer.add( 
44951             {
44952                 text : '&gt;',
44953                 xtype: 'Button',
44954                 handler : function() {
44955                     // no animation..
44956                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44957                 }
44958             }
44959         );
44960         var fel = Roo.get(footDisp.el);
44961         fel.addClass('x-editor-context');
44962         this.footDispWrap = fel; 
44963         this.footDispWrap.overflow  = 'hidden';
44964         
44965         this.footDisp = fel.createChild();
44966         this.footDispWrap.on('click', this.onContextClick, this)
44967         
44968         
44969     },
44970     onContextClick : function (ev,dom)
44971     {
44972         ev.preventDefault();
44973         var  cn = dom.className;
44974         //Roo.log(cn);
44975         if (!cn.match(/x-ed-loc-/)) {
44976             return;
44977         }
44978         var n = cn.split('-').pop();
44979         var ans = this.footerEls;
44980         var sel = ans[n];
44981         
44982          // pick
44983         var range = this.editorcore.createRange();
44984         
44985         range.selectNodeContents(sel);
44986         //range.selectNode(sel);
44987         
44988         
44989         var selection = this.editorcore.getSelection();
44990         selection.removeAllRanges();
44991         selection.addRange(range);
44992         
44993         
44994         
44995         this.updateToolbar(null, null, sel);
44996         
44997         
44998     }
44999     
45000     
45001     
45002     
45003     
45004 });
45005
45006
45007
45008
45009
45010 /*
45011  * Based on:
45012  * Ext JS Library 1.1.1
45013  * Copyright(c) 2006-2007, Ext JS, LLC.
45014  *
45015  * Originally Released Under LGPL - original licence link has changed is not relivant.
45016  *
45017  * Fork - LGPL
45018  * <script type="text/javascript">
45019  */
45020  
45021 /**
45022  * @class Roo.form.BasicForm
45023  * @extends Roo.util.Observable
45024  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
45025  * @constructor
45026  * @param {String/HTMLElement/Roo.Element} el The form element or its id
45027  * @param {Object} config Configuration options
45028  */
45029 Roo.form.BasicForm = function(el, config){
45030     this.allItems = [];
45031     this.childForms = [];
45032     Roo.apply(this, config);
45033     /*
45034      * The Roo.form.Field items in this form.
45035      * @type MixedCollection
45036      */
45037      
45038      
45039     this.items = new Roo.util.MixedCollection(false, function(o){
45040         return o.id || (o.id = Roo.id());
45041     });
45042     this.addEvents({
45043         /**
45044          * @event beforeaction
45045          * Fires before any action is performed. Return false to cancel the action.
45046          * @param {Form} this
45047          * @param {Action} action The action to be performed
45048          */
45049         beforeaction: true,
45050         /**
45051          * @event actionfailed
45052          * Fires when an action fails.
45053          * @param {Form} this
45054          * @param {Action} action The action that failed
45055          */
45056         actionfailed : true,
45057         /**
45058          * @event actioncomplete
45059          * Fires when an action is completed.
45060          * @param {Form} this
45061          * @param {Action} action The action that completed
45062          */
45063         actioncomplete : true
45064     });
45065     if(el){
45066         this.initEl(el);
45067     }
45068     Roo.form.BasicForm.superclass.constructor.call(this);
45069 };
45070
45071 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
45072     /**
45073      * @cfg {String} method
45074      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
45075      */
45076     /**
45077      * @cfg {DataReader} reader
45078      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
45079      * This is optional as there is built-in support for processing JSON.
45080      */
45081     /**
45082      * @cfg {DataReader} errorReader
45083      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45084      * This is completely optional as there is built-in support for processing JSON.
45085      */
45086     /**
45087      * @cfg {String} url
45088      * The URL to use for form actions if one isn't supplied in the action options.
45089      */
45090     /**
45091      * @cfg {Boolean} fileUpload
45092      * Set to true if this form is a file upload.
45093      */
45094      
45095     /**
45096      * @cfg {Object} baseParams
45097      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45098      */
45099      /**
45100      
45101     /**
45102      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45103      */
45104     timeout: 30,
45105
45106     // private
45107     activeAction : null,
45108
45109     /**
45110      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45111      * or setValues() data instead of when the form was first created.
45112      */
45113     trackResetOnLoad : false,
45114     
45115     
45116     /**
45117      * childForms - used for multi-tab forms
45118      * @type {Array}
45119      */
45120     childForms : false,
45121     
45122     /**
45123      * allItems - full list of fields.
45124      * @type {Array}
45125      */
45126     allItems : false,
45127     
45128     /**
45129      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45130      * element by passing it or its id or mask the form itself by passing in true.
45131      * @type Mixed
45132      */
45133     waitMsgTarget : false,
45134
45135     // private
45136     initEl : function(el){
45137         this.el = Roo.get(el);
45138         this.id = this.el.id || Roo.id();
45139         this.el.on('submit', this.onSubmit, this);
45140         this.el.addClass('x-form');
45141     },
45142
45143     // private
45144     onSubmit : function(e){
45145         e.stopEvent();
45146     },
45147
45148     /**
45149      * Returns true if client-side validation on the form is successful.
45150      * @return Boolean
45151      */
45152     isValid : function(){
45153         var valid = true;
45154         this.items.each(function(f){
45155            if(!f.validate()){
45156                valid = false;
45157            }
45158         });
45159         return valid;
45160     },
45161
45162     /**
45163      * Returns true if any fields in this form have changed since their original load.
45164      * @return Boolean
45165      */
45166     isDirty : function(){
45167         var dirty = false;
45168         this.items.each(function(f){
45169            if(f.isDirty()){
45170                dirty = true;
45171                return false;
45172            }
45173         });
45174         return dirty;
45175     },
45176
45177     /**
45178      * Performs a predefined action (submit or load) or custom actions you define on this form.
45179      * @param {String} actionName The name of the action type
45180      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45181      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45182      * accept other config options):
45183      * <pre>
45184 Property          Type             Description
45185 ----------------  ---------------  ----------------------------------------------------------------------------------
45186 url               String           The url for the action (defaults to the form's url)
45187 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45188 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45189 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45190                                    validate the form on the client (defaults to false)
45191      * </pre>
45192      * @return {BasicForm} this
45193      */
45194     doAction : function(action, options){
45195         if(typeof action == 'string'){
45196             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45197         }
45198         if(this.fireEvent('beforeaction', this, action) !== false){
45199             this.beforeAction(action);
45200             action.run.defer(100, action);
45201         }
45202         return this;
45203     },
45204
45205     /**
45206      * Shortcut to do a submit action.
45207      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45208      * @return {BasicForm} this
45209      */
45210     submit : function(options){
45211         this.doAction('submit', options);
45212         return this;
45213     },
45214
45215     /**
45216      * Shortcut to do a load action.
45217      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45218      * @return {BasicForm} this
45219      */
45220     load : function(options){
45221         this.doAction('load', options);
45222         return this;
45223     },
45224
45225     /**
45226      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45227      * @param {Record} record The record to edit
45228      * @return {BasicForm} this
45229      */
45230     updateRecord : function(record){
45231         record.beginEdit();
45232         var fs = record.fields;
45233         fs.each(function(f){
45234             var field = this.findField(f.name);
45235             if(field){
45236                 record.set(f.name, field.getValue());
45237             }
45238         }, this);
45239         record.endEdit();
45240         return this;
45241     },
45242
45243     /**
45244      * Loads an Roo.data.Record into this form.
45245      * @param {Record} record The record to load
45246      * @return {BasicForm} this
45247      */
45248     loadRecord : function(record){
45249         this.setValues(record.data);
45250         return this;
45251     },
45252
45253     // private
45254     beforeAction : function(action){
45255         var o = action.options;
45256         
45257        
45258         if(this.waitMsgTarget === true){
45259             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45260         }else if(this.waitMsgTarget){
45261             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45262             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45263         }else {
45264             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45265         }
45266          
45267     },
45268
45269     // private
45270     afterAction : function(action, success){
45271         this.activeAction = null;
45272         var o = action.options;
45273         
45274         if(this.waitMsgTarget === true){
45275             this.el.unmask();
45276         }else if(this.waitMsgTarget){
45277             this.waitMsgTarget.unmask();
45278         }else{
45279             Roo.MessageBox.updateProgress(1);
45280             Roo.MessageBox.hide();
45281         }
45282          
45283         if(success){
45284             if(o.reset){
45285                 this.reset();
45286             }
45287             Roo.callback(o.success, o.scope, [this, action]);
45288             this.fireEvent('actioncomplete', this, action);
45289             
45290         }else{
45291             
45292             // failure condition..
45293             // we have a scenario where updates need confirming.
45294             // eg. if a locking scenario exists..
45295             // we look for { errors : { needs_confirm : true }} in the response.
45296             if (
45297                 (typeof(action.result) != 'undefined')  &&
45298                 (typeof(action.result.errors) != 'undefined')  &&
45299                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45300            ){
45301                 var _t = this;
45302                 Roo.MessageBox.confirm(
45303                     "Change requires confirmation",
45304                     action.result.errorMsg,
45305                     function(r) {
45306                         if (r != 'yes') {
45307                             return;
45308                         }
45309                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45310                     }
45311                     
45312                 );
45313                 
45314                 
45315                 
45316                 return;
45317             }
45318             
45319             Roo.callback(o.failure, o.scope, [this, action]);
45320             // show an error message if no failed handler is set..
45321             if (!this.hasListener('actionfailed')) {
45322                 Roo.MessageBox.alert("Error",
45323                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45324                         action.result.errorMsg :
45325                         "Saving Failed, please check your entries or try again"
45326                 );
45327             }
45328             
45329             this.fireEvent('actionfailed', this, action);
45330         }
45331         
45332     },
45333
45334     /**
45335      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45336      * @param {String} id The value to search for
45337      * @return Field
45338      */
45339     findField : function(id){
45340         var field = this.items.get(id);
45341         if(!field){
45342             this.items.each(function(f){
45343                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45344                     field = f;
45345                     return false;
45346                 }
45347             });
45348         }
45349         return field || null;
45350     },
45351
45352     /**
45353      * Add a secondary form to this one, 
45354      * Used to provide tabbed forms. One form is primary, with hidden values 
45355      * which mirror the elements from the other forms.
45356      * 
45357      * @param {Roo.form.Form} form to add.
45358      * 
45359      */
45360     addForm : function(form)
45361     {
45362        
45363         if (this.childForms.indexOf(form) > -1) {
45364             // already added..
45365             return;
45366         }
45367         this.childForms.push(form);
45368         var n = '';
45369         Roo.each(form.allItems, function (fe) {
45370             
45371             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45372             if (this.findField(n)) { // already added..
45373                 return;
45374             }
45375             var add = new Roo.form.Hidden({
45376                 name : n
45377             });
45378             add.render(this.el);
45379             
45380             this.add( add );
45381         }, this);
45382         
45383     },
45384     /**
45385      * Mark fields in this form invalid in bulk.
45386      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45387      * @return {BasicForm} this
45388      */
45389     markInvalid : function(errors){
45390         if(errors instanceof Array){
45391             for(var i = 0, len = errors.length; i < len; i++){
45392                 var fieldError = errors[i];
45393                 var f = this.findField(fieldError.id);
45394                 if(f){
45395                     f.markInvalid(fieldError.msg);
45396                 }
45397             }
45398         }else{
45399             var field, id;
45400             for(id in errors){
45401                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45402                     field.markInvalid(errors[id]);
45403                 }
45404             }
45405         }
45406         Roo.each(this.childForms || [], function (f) {
45407             f.markInvalid(errors);
45408         });
45409         
45410         return this;
45411     },
45412
45413     /**
45414      * Set values for fields in this form in bulk.
45415      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45416      * @return {BasicForm} this
45417      */
45418     setValues : function(values){
45419         if(values instanceof Array){ // array of objects
45420             for(var i = 0, len = values.length; i < len; i++){
45421                 var v = values[i];
45422                 var f = this.findField(v.id);
45423                 if(f){
45424                     f.setValue(v.value);
45425                     if(this.trackResetOnLoad){
45426                         f.originalValue = f.getValue();
45427                     }
45428                 }
45429             }
45430         }else{ // object hash
45431             var field, id;
45432             for(id in values){
45433                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45434                     
45435                     if (field.setFromData && 
45436                         field.valueField && 
45437                         field.displayField &&
45438                         // combos' with local stores can 
45439                         // be queried via setValue()
45440                         // to set their value..
45441                         (field.store && !field.store.isLocal)
45442                         ) {
45443                         // it's a combo
45444                         var sd = { };
45445                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45446                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45447                         field.setFromData(sd);
45448                         
45449                     } else {
45450                         field.setValue(values[id]);
45451                     }
45452                     
45453                     
45454                     if(this.trackResetOnLoad){
45455                         field.originalValue = field.getValue();
45456                     }
45457                 }
45458             }
45459         }
45460          
45461         Roo.each(this.childForms || [], function (f) {
45462             f.setValues(values);
45463         });
45464                 
45465         return this;
45466     },
45467
45468     /**
45469      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45470      * they are returned as an array.
45471      * @param {Boolean} asString
45472      * @return {Object}
45473      */
45474     getValues : function(asString){
45475         if (this.childForms) {
45476             // copy values from the child forms
45477             Roo.each(this.childForms, function (f) {
45478                 this.setValues(f.getValues());
45479             }, this);
45480         }
45481         
45482         
45483         
45484         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45485         if(asString === true){
45486             return fs;
45487         }
45488         return Roo.urlDecode(fs);
45489     },
45490     
45491     /**
45492      * Returns the fields in this form as an object with key/value pairs. 
45493      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45494      * @return {Object}
45495      */
45496     getFieldValues : function(with_hidden)
45497     {
45498         if (this.childForms) {
45499             // copy values from the child forms
45500             // should this call getFieldValues - probably not as we do not currently copy
45501             // hidden fields when we generate..
45502             Roo.each(this.childForms, function (f) {
45503                 this.setValues(f.getValues());
45504             }, this);
45505         }
45506         
45507         var ret = {};
45508         this.items.each(function(f){
45509             if (!f.getName()) {
45510                 return;
45511             }
45512             var v = f.getValue();
45513             if (f.inputType =='radio') {
45514                 if (typeof(ret[f.getName()]) == 'undefined') {
45515                     ret[f.getName()] = ''; // empty..
45516                 }
45517                 
45518                 if (!f.el.dom.checked) {
45519                     return;
45520                     
45521                 }
45522                 v = f.el.dom.value;
45523                 
45524             }
45525             
45526             // not sure if this supported any more..
45527             if ((typeof(v) == 'object') && f.getRawValue) {
45528                 v = f.getRawValue() ; // dates..
45529             }
45530             // combo boxes where name != hiddenName...
45531             if (f.name != f.getName()) {
45532                 ret[f.name] = f.getRawValue();
45533             }
45534             ret[f.getName()] = v;
45535         });
45536         
45537         return ret;
45538     },
45539
45540     /**
45541      * Clears all invalid messages in this form.
45542      * @return {BasicForm} this
45543      */
45544     clearInvalid : function(){
45545         this.items.each(function(f){
45546            f.clearInvalid();
45547         });
45548         
45549         Roo.each(this.childForms || [], function (f) {
45550             f.clearInvalid();
45551         });
45552         
45553         
45554         return this;
45555     },
45556
45557     /**
45558      * Resets this form.
45559      * @return {BasicForm} this
45560      */
45561     reset : function(){
45562         this.items.each(function(f){
45563             f.reset();
45564         });
45565         
45566         Roo.each(this.childForms || [], function (f) {
45567             f.reset();
45568         });
45569        
45570         
45571         return this;
45572     },
45573
45574     /**
45575      * Add Roo.form components to this form.
45576      * @param {Field} field1
45577      * @param {Field} field2 (optional)
45578      * @param {Field} etc (optional)
45579      * @return {BasicForm} this
45580      */
45581     add : function(){
45582         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45583         return this;
45584     },
45585
45586
45587     /**
45588      * Removes a field from the items collection (does NOT remove its markup).
45589      * @param {Field} field
45590      * @return {BasicForm} this
45591      */
45592     remove : function(field){
45593         this.items.remove(field);
45594         return this;
45595     },
45596
45597     /**
45598      * Looks at the fields in this form, checks them for an id attribute,
45599      * and calls applyTo on the existing dom element with that id.
45600      * @return {BasicForm} this
45601      */
45602     render : function(){
45603         this.items.each(function(f){
45604             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45605                 f.applyTo(f.id);
45606             }
45607         });
45608         return this;
45609     },
45610
45611     /**
45612      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45613      * @param {Object} values
45614      * @return {BasicForm} this
45615      */
45616     applyToFields : function(o){
45617         this.items.each(function(f){
45618            Roo.apply(f, o);
45619         });
45620         return this;
45621     },
45622
45623     /**
45624      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45625      * @param {Object} values
45626      * @return {BasicForm} this
45627      */
45628     applyIfToFields : function(o){
45629         this.items.each(function(f){
45630            Roo.applyIf(f, o);
45631         });
45632         return this;
45633     }
45634 });
45635
45636 // back compat
45637 Roo.BasicForm = Roo.form.BasicForm;/*
45638  * Based on:
45639  * Ext JS Library 1.1.1
45640  * Copyright(c) 2006-2007, Ext JS, LLC.
45641  *
45642  * Originally Released Under LGPL - original licence link has changed is not relivant.
45643  *
45644  * Fork - LGPL
45645  * <script type="text/javascript">
45646  */
45647
45648 /**
45649  * @class Roo.form.Form
45650  * @extends Roo.form.BasicForm
45651  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45652  * @constructor
45653  * @param {Object} config Configuration options
45654  */
45655 Roo.form.Form = function(config){
45656     var xitems =  [];
45657     if (config.items) {
45658         xitems = config.items;
45659         delete config.items;
45660     }
45661    
45662     
45663     Roo.form.Form.superclass.constructor.call(this, null, config);
45664     this.url = this.url || this.action;
45665     if(!this.root){
45666         this.root = new Roo.form.Layout(Roo.applyIf({
45667             id: Roo.id()
45668         }, config));
45669     }
45670     this.active = this.root;
45671     /**
45672      * Array of all the buttons that have been added to this form via {@link addButton}
45673      * @type Array
45674      */
45675     this.buttons = [];
45676     this.allItems = [];
45677     this.addEvents({
45678         /**
45679          * @event clientvalidation
45680          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45681          * @param {Form} this
45682          * @param {Boolean} valid true if the form has passed client-side validation
45683          */
45684         clientvalidation: true,
45685         /**
45686          * @event rendered
45687          * Fires when the form is rendered
45688          * @param {Roo.form.Form} form
45689          */
45690         rendered : true
45691     });
45692     
45693     if (this.progressUrl) {
45694             // push a hidden field onto the list of fields..
45695             this.addxtype( {
45696                     xns: Roo.form, 
45697                     xtype : 'Hidden', 
45698                     name : 'UPLOAD_IDENTIFIER' 
45699             });
45700         }
45701         
45702     
45703     Roo.each(xitems, this.addxtype, this);
45704     
45705     
45706     
45707 };
45708
45709 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45710     /**
45711      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45712      */
45713     /**
45714      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45715      */
45716     /**
45717      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45718      */
45719     buttonAlign:'center',
45720
45721     /**
45722      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45723      */
45724     minButtonWidth:75,
45725
45726     /**
45727      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45728      * This property cascades to child containers if not set.
45729      */
45730     labelAlign:'left',
45731
45732     /**
45733      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45734      * fires a looping event with that state. This is required to bind buttons to the valid
45735      * state using the config value formBind:true on the button.
45736      */
45737     monitorValid : false,
45738
45739     /**
45740      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45741      */
45742     monitorPoll : 200,
45743     
45744     /**
45745      * @cfg {String} progressUrl - Url to return progress data 
45746      */
45747     
45748     progressUrl : false,
45749   
45750     /**
45751      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45752      * fields are added and the column is closed. If no fields are passed the column remains open
45753      * until end() is called.
45754      * @param {Object} config The config to pass to the column
45755      * @param {Field} field1 (optional)
45756      * @param {Field} field2 (optional)
45757      * @param {Field} etc (optional)
45758      * @return Column The column container object
45759      */
45760     column : function(c){
45761         var col = new Roo.form.Column(c);
45762         this.start(col);
45763         if(arguments.length > 1){ // duplicate code required because of Opera
45764             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45765             this.end();
45766         }
45767         return col;
45768     },
45769
45770     /**
45771      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45772      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45773      * until end() is called.
45774      * @param {Object} config The config to pass to the fieldset
45775      * @param {Field} field1 (optional)
45776      * @param {Field} field2 (optional)
45777      * @param {Field} etc (optional)
45778      * @return FieldSet The fieldset container object
45779      */
45780     fieldset : function(c){
45781         var fs = new Roo.form.FieldSet(c);
45782         this.start(fs);
45783         if(arguments.length > 1){ // duplicate code required because of Opera
45784             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45785             this.end();
45786         }
45787         return fs;
45788     },
45789
45790     /**
45791      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45792      * fields are added and the container is closed. If no fields are passed the container remains open
45793      * until end() is called.
45794      * @param {Object} config The config to pass to the Layout
45795      * @param {Field} field1 (optional)
45796      * @param {Field} field2 (optional)
45797      * @param {Field} etc (optional)
45798      * @return Layout The container object
45799      */
45800     container : function(c){
45801         var l = new Roo.form.Layout(c);
45802         this.start(l);
45803         if(arguments.length > 1){ // duplicate code required because of Opera
45804             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45805             this.end();
45806         }
45807         return l;
45808     },
45809
45810     /**
45811      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45812      * @param {Object} container A Roo.form.Layout or subclass of Layout
45813      * @return {Form} this
45814      */
45815     start : function(c){
45816         // cascade label info
45817         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45818         this.active.stack.push(c);
45819         c.ownerCt = this.active;
45820         this.active = c;
45821         return this;
45822     },
45823
45824     /**
45825      * Closes the current open container
45826      * @return {Form} this
45827      */
45828     end : function(){
45829         if(this.active == this.root){
45830             return this;
45831         }
45832         this.active = this.active.ownerCt;
45833         return this;
45834     },
45835
45836     /**
45837      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45838      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45839      * as the label of the field.
45840      * @param {Field} field1
45841      * @param {Field} field2 (optional)
45842      * @param {Field} etc. (optional)
45843      * @return {Form} this
45844      */
45845     add : function(){
45846         this.active.stack.push.apply(this.active.stack, arguments);
45847         this.allItems.push.apply(this.allItems,arguments);
45848         var r = [];
45849         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45850             if(a[i].isFormField){
45851                 r.push(a[i]);
45852             }
45853         }
45854         if(r.length > 0){
45855             Roo.form.Form.superclass.add.apply(this, r);
45856         }
45857         return this;
45858     },
45859     
45860
45861     
45862     
45863     
45864      /**
45865      * Find any element that has been added to a form, using it's ID or name
45866      * This can include framesets, columns etc. along with regular fields..
45867      * @param {String} id - id or name to find.
45868      
45869      * @return {Element} e - or false if nothing found.
45870      */
45871     findbyId : function(id)
45872     {
45873         var ret = false;
45874         if (!id) {
45875             return ret;
45876         }
45877         Roo.each(this.allItems, function(f){
45878             if (f.id == id || f.name == id ){
45879                 ret = f;
45880                 return false;
45881             }
45882         });
45883         return ret;
45884     },
45885
45886     
45887     
45888     /**
45889      * Render this form into the passed container. This should only be called once!
45890      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45891      * @return {Form} this
45892      */
45893     render : function(ct)
45894     {
45895         
45896         
45897         
45898         ct = Roo.get(ct);
45899         var o = this.autoCreate || {
45900             tag: 'form',
45901             method : this.method || 'POST',
45902             id : this.id || Roo.id()
45903         };
45904         this.initEl(ct.createChild(o));
45905
45906         this.root.render(this.el);
45907         
45908        
45909              
45910         this.items.each(function(f){
45911             f.render('x-form-el-'+f.id);
45912         });
45913
45914         if(this.buttons.length > 0){
45915             // tables are required to maintain order and for correct IE layout
45916             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45917                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45918                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45919             }}, null, true);
45920             var tr = tb.getElementsByTagName('tr')[0];
45921             for(var i = 0, len = this.buttons.length; i < len; i++) {
45922                 var b = this.buttons[i];
45923                 var td = document.createElement('td');
45924                 td.className = 'x-form-btn-td';
45925                 b.render(tr.appendChild(td));
45926             }
45927         }
45928         if(this.monitorValid){ // initialize after render
45929             this.startMonitoring();
45930         }
45931         this.fireEvent('rendered', this);
45932         return this;
45933     },
45934
45935     /**
45936      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45937      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45938      * object or a valid Roo.DomHelper element config
45939      * @param {Function} handler The function called when the button is clicked
45940      * @param {Object} scope (optional) The scope of the handler function
45941      * @return {Roo.Button}
45942      */
45943     addButton : function(config, handler, scope){
45944         var bc = {
45945             handler: handler,
45946             scope: scope,
45947             minWidth: this.minButtonWidth,
45948             hideParent:true
45949         };
45950         if(typeof config == "string"){
45951             bc.text = config;
45952         }else{
45953             Roo.apply(bc, config);
45954         }
45955         var btn = new Roo.Button(null, bc);
45956         this.buttons.push(btn);
45957         return btn;
45958     },
45959
45960      /**
45961      * Adds a series of form elements (using the xtype property as the factory method.
45962      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45963      * @param {Object} config 
45964      */
45965     
45966     addxtype : function()
45967     {
45968         var ar = Array.prototype.slice.call(arguments, 0);
45969         var ret = false;
45970         for(var i = 0; i < ar.length; i++) {
45971             if (!ar[i]) {
45972                 continue; // skip -- if this happends something invalid got sent, we 
45973                 // should ignore it, as basically that interface element will not show up
45974                 // and that should be pretty obvious!!
45975             }
45976             
45977             if (Roo.form[ar[i].xtype]) {
45978                 ar[i].form = this;
45979                 var fe = Roo.factory(ar[i], Roo.form);
45980                 if (!ret) {
45981                     ret = fe;
45982                 }
45983                 fe.form = this;
45984                 if (fe.store) {
45985                     fe.store.form = this;
45986                 }
45987                 if (fe.isLayout) {  
45988                          
45989                     this.start(fe);
45990                     this.allItems.push(fe);
45991                     if (fe.items && fe.addxtype) {
45992                         fe.addxtype.apply(fe, fe.items);
45993                         delete fe.items;
45994                     }
45995                      this.end();
45996                     continue;
45997                 }
45998                 
45999                 
46000                  
46001                 this.add(fe);
46002               //  console.log('adding ' + ar[i].xtype);
46003             }
46004             if (ar[i].xtype == 'Button') {  
46005                 //console.log('adding button');
46006                 //console.log(ar[i]);
46007                 this.addButton(ar[i]);
46008                 this.allItems.push(fe);
46009                 continue;
46010             }
46011             
46012             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
46013                 alert('end is not supported on xtype any more, use items');
46014             //    this.end();
46015             //    //console.log('adding end');
46016             }
46017             
46018         }
46019         return ret;
46020     },
46021     
46022     /**
46023      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
46024      * option "monitorValid"
46025      */
46026     startMonitoring : function(){
46027         if(!this.bound){
46028             this.bound = true;
46029             Roo.TaskMgr.start({
46030                 run : this.bindHandler,
46031                 interval : this.monitorPoll || 200,
46032                 scope: this
46033             });
46034         }
46035     },
46036
46037     /**
46038      * Stops monitoring of the valid state of this form
46039      */
46040     stopMonitoring : function(){
46041         this.bound = false;
46042     },
46043
46044     // private
46045     bindHandler : function(){
46046         if(!this.bound){
46047             return false; // stops binding
46048         }
46049         var valid = true;
46050         this.items.each(function(f){
46051             if(!f.isValid(true)){
46052                 valid = false;
46053                 return false;
46054             }
46055         });
46056         for(var i = 0, len = this.buttons.length; i < len; i++){
46057             var btn = this.buttons[i];
46058             if(btn.formBind === true && btn.disabled === valid){
46059                 btn.setDisabled(!valid);
46060             }
46061         }
46062         this.fireEvent('clientvalidation', this, valid);
46063     }
46064     
46065     
46066     
46067     
46068     
46069     
46070     
46071     
46072 });
46073
46074
46075 // back compat
46076 Roo.Form = Roo.form.Form;
46077 /*
46078  * Based on:
46079  * Ext JS Library 1.1.1
46080  * Copyright(c) 2006-2007, Ext JS, LLC.
46081  *
46082  * Originally Released Under LGPL - original licence link has changed is not relivant.
46083  *
46084  * Fork - LGPL
46085  * <script type="text/javascript">
46086  */
46087
46088 // as we use this in bootstrap.
46089 Roo.namespace('Roo.form');
46090  /**
46091  * @class Roo.form.Action
46092  * Internal Class used to handle form actions
46093  * @constructor
46094  * @param {Roo.form.BasicForm} el The form element or its id
46095  * @param {Object} config Configuration options
46096  */
46097
46098  
46099  
46100 // define the action interface
46101 Roo.form.Action = function(form, options){
46102     this.form = form;
46103     this.options = options || {};
46104 };
46105 /**
46106  * Client Validation Failed
46107  * @const 
46108  */
46109 Roo.form.Action.CLIENT_INVALID = 'client';
46110 /**
46111  * Server Validation Failed
46112  * @const 
46113  */
46114 Roo.form.Action.SERVER_INVALID = 'server';
46115  /**
46116  * Connect to Server Failed
46117  * @const 
46118  */
46119 Roo.form.Action.CONNECT_FAILURE = 'connect';
46120 /**
46121  * Reading Data from Server Failed
46122  * @const 
46123  */
46124 Roo.form.Action.LOAD_FAILURE = 'load';
46125
46126 Roo.form.Action.prototype = {
46127     type : 'default',
46128     failureType : undefined,
46129     response : undefined,
46130     result : undefined,
46131
46132     // interface method
46133     run : function(options){
46134
46135     },
46136
46137     // interface method
46138     success : function(response){
46139
46140     },
46141
46142     // interface method
46143     handleResponse : function(response){
46144
46145     },
46146
46147     // default connection failure
46148     failure : function(response){
46149         
46150         this.response = response;
46151         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46152         this.form.afterAction(this, false);
46153     },
46154
46155     processResponse : function(response){
46156         this.response = response;
46157         if(!response.responseText){
46158             return true;
46159         }
46160         this.result = this.handleResponse(response);
46161         return this.result;
46162     },
46163
46164     // utility functions used internally
46165     getUrl : function(appendParams){
46166         var url = this.options.url || this.form.url || this.form.el.dom.action;
46167         if(appendParams){
46168             var p = this.getParams();
46169             if(p){
46170                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46171             }
46172         }
46173         return url;
46174     },
46175
46176     getMethod : function(){
46177         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46178     },
46179
46180     getParams : function(){
46181         var bp = this.form.baseParams;
46182         var p = this.options.params;
46183         if(p){
46184             if(typeof p == "object"){
46185                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46186             }else if(typeof p == 'string' && bp){
46187                 p += '&' + Roo.urlEncode(bp);
46188             }
46189         }else if(bp){
46190             p = Roo.urlEncode(bp);
46191         }
46192         return p;
46193     },
46194
46195     createCallback : function(){
46196         return {
46197             success: this.success,
46198             failure: this.failure,
46199             scope: this,
46200             timeout: (this.form.timeout*1000),
46201             upload: this.form.fileUpload ? this.success : undefined
46202         };
46203     }
46204 };
46205
46206 Roo.form.Action.Submit = function(form, options){
46207     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46208 };
46209
46210 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46211     type : 'submit',
46212
46213     haveProgress : false,
46214     uploadComplete : false,
46215     
46216     // uploadProgress indicator.
46217     uploadProgress : function()
46218     {
46219         if (!this.form.progressUrl) {
46220             return;
46221         }
46222         
46223         if (!this.haveProgress) {
46224             Roo.MessageBox.progress("Uploading", "Uploading");
46225         }
46226         if (this.uploadComplete) {
46227            Roo.MessageBox.hide();
46228            return;
46229         }
46230         
46231         this.haveProgress = true;
46232    
46233         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46234         
46235         var c = new Roo.data.Connection();
46236         c.request({
46237             url : this.form.progressUrl,
46238             params: {
46239                 id : uid
46240             },
46241             method: 'GET',
46242             success : function(req){
46243                //console.log(data);
46244                 var rdata = false;
46245                 var edata;
46246                 try  {
46247                    rdata = Roo.decode(req.responseText)
46248                 } catch (e) {
46249                     Roo.log("Invalid data from server..");
46250                     Roo.log(edata);
46251                     return;
46252                 }
46253                 if (!rdata || !rdata.success) {
46254                     Roo.log(rdata);
46255                     Roo.MessageBox.alert(Roo.encode(rdata));
46256                     return;
46257                 }
46258                 var data = rdata.data;
46259                 
46260                 if (this.uploadComplete) {
46261                    Roo.MessageBox.hide();
46262                    return;
46263                 }
46264                    
46265                 if (data){
46266                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46267                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46268                     );
46269                 }
46270                 this.uploadProgress.defer(2000,this);
46271             },
46272        
46273             failure: function(data) {
46274                 Roo.log('progress url failed ');
46275                 Roo.log(data);
46276             },
46277             scope : this
46278         });
46279            
46280     },
46281     
46282     
46283     run : function()
46284     {
46285         // run get Values on the form, so it syncs any secondary forms.
46286         this.form.getValues();
46287         
46288         var o = this.options;
46289         var method = this.getMethod();
46290         var isPost = method == 'POST';
46291         if(o.clientValidation === false || this.form.isValid()){
46292             
46293             if (this.form.progressUrl) {
46294                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46295                     (new Date() * 1) + '' + Math.random());
46296                     
46297             } 
46298             
46299             
46300             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46301                 form:this.form.el.dom,
46302                 url:this.getUrl(!isPost),
46303                 method: method,
46304                 params:isPost ? this.getParams() : null,
46305                 isUpload: this.form.fileUpload
46306             }));
46307             
46308             this.uploadProgress();
46309
46310         }else if (o.clientValidation !== false){ // client validation failed
46311             this.failureType = Roo.form.Action.CLIENT_INVALID;
46312             this.form.afterAction(this, false);
46313         }
46314     },
46315
46316     success : function(response)
46317     {
46318         this.uploadComplete= true;
46319         if (this.haveProgress) {
46320             Roo.MessageBox.hide();
46321         }
46322         
46323         
46324         var result = this.processResponse(response);
46325         if(result === true || result.success){
46326             this.form.afterAction(this, true);
46327             return;
46328         }
46329         if(result.errors){
46330             this.form.markInvalid(result.errors);
46331             this.failureType = Roo.form.Action.SERVER_INVALID;
46332         }
46333         this.form.afterAction(this, false);
46334     },
46335     failure : function(response)
46336     {
46337         this.uploadComplete= true;
46338         if (this.haveProgress) {
46339             Roo.MessageBox.hide();
46340         }
46341         
46342         this.response = response;
46343         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46344         this.form.afterAction(this, false);
46345     },
46346     
46347     handleResponse : function(response){
46348         if(this.form.errorReader){
46349             var rs = this.form.errorReader.read(response);
46350             var errors = [];
46351             if(rs.records){
46352                 for(var i = 0, len = rs.records.length; i < len; i++) {
46353                     var r = rs.records[i];
46354                     errors[i] = r.data;
46355                 }
46356             }
46357             if(errors.length < 1){
46358                 errors = null;
46359             }
46360             return {
46361                 success : rs.success,
46362                 errors : errors
46363             };
46364         }
46365         var ret = false;
46366         try {
46367             ret = Roo.decode(response.responseText);
46368         } catch (e) {
46369             ret = {
46370                 success: false,
46371                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46372                 errors : []
46373             };
46374         }
46375         return ret;
46376         
46377     }
46378 });
46379
46380
46381 Roo.form.Action.Load = function(form, options){
46382     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46383     this.reader = this.form.reader;
46384 };
46385
46386 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46387     type : 'load',
46388
46389     run : function(){
46390         
46391         Roo.Ajax.request(Roo.apply(
46392                 this.createCallback(), {
46393                     method:this.getMethod(),
46394                     url:this.getUrl(false),
46395                     params:this.getParams()
46396         }));
46397     },
46398
46399     success : function(response){
46400         
46401         var result = this.processResponse(response);
46402         if(result === true || !result.success || !result.data){
46403             this.failureType = Roo.form.Action.LOAD_FAILURE;
46404             this.form.afterAction(this, false);
46405             return;
46406         }
46407         this.form.clearInvalid();
46408         this.form.setValues(result.data);
46409         this.form.afterAction(this, true);
46410     },
46411
46412     handleResponse : function(response){
46413         if(this.form.reader){
46414             var rs = this.form.reader.read(response);
46415             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46416             return {
46417                 success : rs.success,
46418                 data : data
46419             };
46420         }
46421         return Roo.decode(response.responseText);
46422     }
46423 });
46424
46425 Roo.form.Action.ACTION_TYPES = {
46426     'load' : Roo.form.Action.Load,
46427     'submit' : Roo.form.Action.Submit
46428 };/*
46429  * Based on:
46430  * Ext JS Library 1.1.1
46431  * Copyright(c) 2006-2007, Ext JS, LLC.
46432  *
46433  * Originally Released Under LGPL - original licence link has changed is not relivant.
46434  *
46435  * Fork - LGPL
46436  * <script type="text/javascript">
46437  */
46438  
46439 /**
46440  * @class Roo.form.Layout
46441  * @extends Roo.Component
46442  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46443  * @constructor
46444  * @param {Object} config Configuration options
46445  */
46446 Roo.form.Layout = function(config){
46447     var xitems = [];
46448     if (config.items) {
46449         xitems = config.items;
46450         delete config.items;
46451     }
46452     Roo.form.Layout.superclass.constructor.call(this, config);
46453     this.stack = [];
46454     Roo.each(xitems, this.addxtype, this);
46455      
46456 };
46457
46458 Roo.extend(Roo.form.Layout, Roo.Component, {
46459     /**
46460      * @cfg {String/Object} autoCreate
46461      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46462      */
46463     /**
46464      * @cfg {String/Object/Function} style
46465      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46466      * a function which returns such a specification.
46467      */
46468     /**
46469      * @cfg {String} labelAlign
46470      * Valid values are "left," "top" and "right" (defaults to "left")
46471      */
46472     /**
46473      * @cfg {Number} labelWidth
46474      * Fixed width in pixels of all field labels (defaults to undefined)
46475      */
46476     /**
46477      * @cfg {Boolean} clear
46478      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46479      */
46480     clear : true,
46481     /**
46482      * @cfg {String} labelSeparator
46483      * The separator to use after field labels (defaults to ':')
46484      */
46485     labelSeparator : ':',
46486     /**
46487      * @cfg {Boolean} hideLabels
46488      * True to suppress the display of field labels in this layout (defaults to false)
46489      */
46490     hideLabels : false,
46491
46492     // private
46493     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46494     
46495     isLayout : true,
46496     
46497     // private
46498     onRender : function(ct, position){
46499         if(this.el){ // from markup
46500             this.el = Roo.get(this.el);
46501         }else {  // generate
46502             var cfg = this.getAutoCreate();
46503             this.el = ct.createChild(cfg, position);
46504         }
46505         if(this.style){
46506             this.el.applyStyles(this.style);
46507         }
46508         if(this.labelAlign){
46509             this.el.addClass('x-form-label-'+this.labelAlign);
46510         }
46511         if(this.hideLabels){
46512             this.labelStyle = "display:none";
46513             this.elementStyle = "padding-left:0;";
46514         }else{
46515             if(typeof this.labelWidth == 'number'){
46516                 this.labelStyle = "width:"+this.labelWidth+"px;";
46517                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46518             }
46519             if(this.labelAlign == 'top'){
46520                 this.labelStyle = "width:auto;";
46521                 this.elementStyle = "padding-left:0;";
46522             }
46523         }
46524         var stack = this.stack;
46525         var slen = stack.length;
46526         if(slen > 0){
46527             if(!this.fieldTpl){
46528                 var t = new Roo.Template(
46529                     '<div class="x-form-item {5}">',
46530                         '<label for="{0}" style="{2}">{1}{4}</label>',
46531                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46532                         '</div>',
46533                     '</div><div class="x-form-clear-left"></div>'
46534                 );
46535                 t.disableFormats = true;
46536                 t.compile();
46537                 Roo.form.Layout.prototype.fieldTpl = t;
46538             }
46539             for(var i = 0; i < slen; i++) {
46540                 if(stack[i].isFormField){
46541                     this.renderField(stack[i]);
46542                 }else{
46543                     this.renderComponent(stack[i]);
46544                 }
46545             }
46546         }
46547         if(this.clear){
46548             this.el.createChild({cls:'x-form-clear'});
46549         }
46550     },
46551
46552     // private
46553     renderField : function(f){
46554         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46555                f.id, //0
46556                f.fieldLabel, //1
46557                f.labelStyle||this.labelStyle||'', //2
46558                this.elementStyle||'', //3
46559                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46560                f.itemCls||this.itemCls||''  //5
46561        ], true).getPrevSibling());
46562     },
46563
46564     // private
46565     renderComponent : function(c){
46566         c.render(c.isLayout ? this.el : this.el.createChild());    
46567     },
46568     /**
46569      * Adds a object form elements (using the xtype property as the factory method.)
46570      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46571      * @param {Object} config 
46572      */
46573     addxtype : function(o)
46574     {
46575         // create the lement.
46576         o.form = this.form;
46577         var fe = Roo.factory(o, Roo.form);
46578         this.form.allItems.push(fe);
46579         this.stack.push(fe);
46580         
46581         if (fe.isFormField) {
46582             this.form.items.add(fe);
46583         }
46584          
46585         return fe;
46586     }
46587 });
46588
46589 /**
46590  * @class Roo.form.Column
46591  * @extends Roo.form.Layout
46592  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46593  * @constructor
46594  * @param {Object} config Configuration options
46595  */
46596 Roo.form.Column = function(config){
46597     Roo.form.Column.superclass.constructor.call(this, config);
46598 };
46599
46600 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46601     /**
46602      * @cfg {Number/String} width
46603      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46604      */
46605     /**
46606      * @cfg {String/Object} autoCreate
46607      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46608      */
46609
46610     // private
46611     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46612
46613     // private
46614     onRender : function(ct, position){
46615         Roo.form.Column.superclass.onRender.call(this, ct, position);
46616         if(this.width){
46617             this.el.setWidth(this.width);
46618         }
46619     }
46620 });
46621
46622
46623 /**
46624  * @class Roo.form.Row
46625  * @extends Roo.form.Layout
46626  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46627  * @constructor
46628  * @param {Object} config Configuration options
46629  */
46630
46631  
46632 Roo.form.Row = function(config){
46633     Roo.form.Row.superclass.constructor.call(this, config);
46634 };
46635  
46636 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46637       /**
46638      * @cfg {Number/String} width
46639      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46640      */
46641     /**
46642      * @cfg {Number/String} height
46643      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46644      */
46645     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46646     
46647     padWidth : 20,
46648     // private
46649     onRender : function(ct, position){
46650         //console.log('row render');
46651         if(!this.rowTpl){
46652             var t = new Roo.Template(
46653                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46654                     '<label for="{0}" style="{2}">{1}{4}</label>',
46655                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46656                     '</div>',
46657                 '</div>'
46658             );
46659             t.disableFormats = true;
46660             t.compile();
46661             Roo.form.Layout.prototype.rowTpl = t;
46662         }
46663         this.fieldTpl = this.rowTpl;
46664         
46665         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46666         var labelWidth = 100;
46667         
46668         if ((this.labelAlign != 'top')) {
46669             if (typeof this.labelWidth == 'number') {
46670                 labelWidth = this.labelWidth
46671             }
46672             this.padWidth =  20 + labelWidth;
46673             
46674         }
46675         
46676         Roo.form.Column.superclass.onRender.call(this, ct, position);
46677         if(this.width){
46678             this.el.setWidth(this.width);
46679         }
46680         if(this.height){
46681             this.el.setHeight(this.height);
46682         }
46683     },
46684     
46685     // private
46686     renderField : function(f){
46687         f.fieldEl = this.fieldTpl.append(this.el, [
46688                f.id, f.fieldLabel,
46689                f.labelStyle||this.labelStyle||'',
46690                this.elementStyle||'',
46691                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46692                f.itemCls||this.itemCls||'',
46693                f.width ? f.width + this.padWidth : 160 + this.padWidth
46694        ],true);
46695     }
46696 });
46697  
46698
46699 /**
46700  * @class Roo.form.FieldSet
46701  * @extends Roo.form.Layout
46702  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46703  * @constructor
46704  * @param {Object} config Configuration options
46705  */
46706 Roo.form.FieldSet = function(config){
46707     Roo.form.FieldSet.superclass.constructor.call(this, config);
46708 };
46709
46710 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46711     /**
46712      * @cfg {String} legend
46713      * The text to display as the legend for the FieldSet (defaults to '')
46714      */
46715     /**
46716      * @cfg {String/Object} autoCreate
46717      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46718      */
46719
46720     // private
46721     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46722
46723     // private
46724     onRender : function(ct, position){
46725         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46726         if(this.legend){
46727             this.setLegend(this.legend);
46728         }
46729     },
46730
46731     // private
46732     setLegend : function(text){
46733         if(this.rendered){
46734             this.el.child('legend').update(text);
46735         }
46736     }
46737 });/*
46738  * Based on:
46739  * Ext JS Library 1.1.1
46740  * Copyright(c) 2006-2007, Ext JS, LLC.
46741  *
46742  * Originally Released Under LGPL - original licence link has changed is not relivant.
46743  *
46744  * Fork - LGPL
46745  * <script type="text/javascript">
46746  */
46747 /**
46748  * @class Roo.form.VTypes
46749  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46750  * @singleton
46751  */
46752 Roo.form.VTypes = function(){
46753     // closure these in so they are only created once.
46754     var alpha = /^[a-zA-Z_]+$/;
46755     var alphanum = /^[a-zA-Z0-9_]+$/;
46756     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46757     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46758
46759     // All these messages and functions are configurable
46760     return {
46761         /**
46762          * The function used to validate email addresses
46763          * @param {String} value The email address
46764          */
46765         'email' : function(v){
46766             return email.test(v);
46767         },
46768         /**
46769          * The error text to display when the email validation function returns false
46770          * @type String
46771          */
46772         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46773         /**
46774          * The keystroke filter mask to be applied on email input
46775          * @type RegExp
46776          */
46777         'emailMask' : /[a-z0-9_\.\-@]/i,
46778
46779         /**
46780          * The function used to validate URLs
46781          * @param {String} value The URL
46782          */
46783         'url' : function(v){
46784             return url.test(v);
46785         },
46786         /**
46787          * The error text to display when the url validation function returns false
46788          * @type String
46789          */
46790         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46791         
46792         /**
46793          * The function used to validate alpha values
46794          * @param {String} value The value
46795          */
46796         'alpha' : function(v){
46797             return alpha.test(v);
46798         },
46799         /**
46800          * The error text to display when the alpha validation function returns false
46801          * @type String
46802          */
46803         'alphaText' : 'This field should only contain letters and _',
46804         /**
46805          * The keystroke filter mask to be applied on alpha input
46806          * @type RegExp
46807          */
46808         'alphaMask' : /[a-z_]/i,
46809
46810         /**
46811          * The function used to validate alphanumeric values
46812          * @param {String} value The value
46813          */
46814         'alphanum' : function(v){
46815             return alphanum.test(v);
46816         },
46817         /**
46818          * The error text to display when the alphanumeric validation function returns false
46819          * @type String
46820          */
46821         'alphanumText' : 'This field should only contain letters, numbers and _',
46822         /**
46823          * The keystroke filter mask to be applied on alphanumeric input
46824          * @type RegExp
46825          */
46826         'alphanumMask' : /[a-z0-9_]/i
46827     };
46828 }();//<script type="text/javascript">
46829
46830 /**
46831  * @class Roo.form.FCKeditor
46832  * @extends Roo.form.TextArea
46833  * Wrapper around the FCKEditor http://www.fckeditor.net
46834  * @constructor
46835  * Creates a new FCKeditor
46836  * @param {Object} config Configuration options
46837  */
46838 Roo.form.FCKeditor = function(config){
46839     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46840     this.addEvents({
46841          /**
46842          * @event editorinit
46843          * Fired when the editor is initialized - you can add extra handlers here..
46844          * @param {FCKeditor} this
46845          * @param {Object} the FCK object.
46846          */
46847         editorinit : true
46848     });
46849     
46850     
46851 };
46852 Roo.form.FCKeditor.editors = { };
46853 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46854 {
46855     //defaultAutoCreate : {
46856     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46857     //},
46858     // private
46859     /**
46860      * @cfg {Object} fck options - see fck manual for details.
46861      */
46862     fckconfig : false,
46863     
46864     /**
46865      * @cfg {Object} fck toolbar set (Basic or Default)
46866      */
46867     toolbarSet : 'Basic',
46868     /**
46869      * @cfg {Object} fck BasePath
46870      */ 
46871     basePath : '/fckeditor/',
46872     
46873     
46874     frame : false,
46875     
46876     value : '',
46877     
46878    
46879     onRender : function(ct, position)
46880     {
46881         if(!this.el){
46882             this.defaultAutoCreate = {
46883                 tag: "textarea",
46884                 style:"width:300px;height:60px;",
46885                 autocomplete: "new-password"
46886             };
46887         }
46888         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46889         /*
46890         if(this.grow){
46891             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46892             if(this.preventScrollbars){
46893                 this.el.setStyle("overflow", "hidden");
46894             }
46895             this.el.setHeight(this.growMin);
46896         }
46897         */
46898         //console.log('onrender' + this.getId() );
46899         Roo.form.FCKeditor.editors[this.getId()] = this;
46900          
46901
46902         this.replaceTextarea() ;
46903         
46904     },
46905     
46906     getEditor : function() {
46907         return this.fckEditor;
46908     },
46909     /**
46910      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46911      * @param {Mixed} value The value to set
46912      */
46913     
46914     
46915     setValue : function(value)
46916     {
46917         //console.log('setValue: ' + value);
46918         
46919         if(typeof(value) == 'undefined') { // not sure why this is happending...
46920             return;
46921         }
46922         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46923         
46924         //if(!this.el || !this.getEditor()) {
46925         //    this.value = value;
46926             //this.setValue.defer(100,this,[value]);    
46927         //    return;
46928         //} 
46929         
46930         if(!this.getEditor()) {
46931             return;
46932         }
46933         
46934         this.getEditor().SetData(value);
46935         
46936         //
46937
46938     },
46939
46940     /**
46941      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46942      * @return {Mixed} value The field value
46943      */
46944     getValue : function()
46945     {
46946         
46947         if (this.frame && this.frame.dom.style.display == 'none') {
46948             return Roo.form.FCKeditor.superclass.getValue.call(this);
46949         }
46950         
46951         if(!this.el || !this.getEditor()) {
46952            
46953            // this.getValue.defer(100,this); 
46954             return this.value;
46955         }
46956        
46957         
46958         var value=this.getEditor().GetData();
46959         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46960         return Roo.form.FCKeditor.superclass.getValue.call(this);
46961         
46962
46963     },
46964
46965     /**
46966      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46967      * @return {Mixed} value The field value
46968      */
46969     getRawValue : function()
46970     {
46971         if (this.frame && this.frame.dom.style.display == 'none') {
46972             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46973         }
46974         
46975         if(!this.el || !this.getEditor()) {
46976             //this.getRawValue.defer(100,this); 
46977             return this.value;
46978             return;
46979         }
46980         
46981         
46982         
46983         var value=this.getEditor().GetData();
46984         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46985         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46986          
46987     },
46988     
46989     setSize : function(w,h) {
46990         
46991         
46992         
46993         //if (this.frame && this.frame.dom.style.display == 'none') {
46994         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46995         //    return;
46996         //}
46997         //if(!this.el || !this.getEditor()) {
46998         //    this.setSize.defer(100,this, [w,h]); 
46999         //    return;
47000         //}
47001         
47002         
47003         
47004         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
47005         
47006         this.frame.dom.setAttribute('width', w);
47007         this.frame.dom.setAttribute('height', h);
47008         this.frame.setSize(w,h);
47009         
47010     },
47011     
47012     toggleSourceEdit : function(value) {
47013         
47014       
47015          
47016         this.el.dom.style.display = value ? '' : 'none';
47017         this.frame.dom.style.display = value ?  'none' : '';
47018         
47019     },
47020     
47021     
47022     focus: function(tag)
47023     {
47024         if (this.frame.dom.style.display == 'none') {
47025             return Roo.form.FCKeditor.superclass.focus.call(this);
47026         }
47027         if(!this.el || !this.getEditor()) {
47028             this.focus.defer(100,this, [tag]); 
47029             return;
47030         }
47031         
47032         
47033         
47034         
47035         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
47036         this.getEditor().Focus();
47037         if (tgs.length) {
47038             if (!this.getEditor().Selection.GetSelection()) {
47039                 this.focus.defer(100,this, [tag]); 
47040                 return;
47041             }
47042             
47043             
47044             var r = this.getEditor().EditorDocument.createRange();
47045             r.setStart(tgs[0],0);
47046             r.setEnd(tgs[0],0);
47047             this.getEditor().Selection.GetSelection().removeAllRanges();
47048             this.getEditor().Selection.GetSelection().addRange(r);
47049             this.getEditor().Focus();
47050         }
47051         
47052     },
47053     
47054     
47055     
47056     replaceTextarea : function()
47057     {
47058         if ( document.getElementById( this.getId() + '___Frame' ) ) {
47059             return ;
47060         }
47061         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
47062         //{
47063             // We must check the elements firstly using the Id and then the name.
47064         var oTextarea = document.getElementById( this.getId() );
47065         
47066         var colElementsByName = document.getElementsByName( this.getId() ) ;
47067          
47068         oTextarea.style.display = 'none' ;
47069
47070         if ( oTextarea.tabIndex ) {            
47071             this.TabIndex = oTextarea.tabIndex ;
47072         }
47073         
47074         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
47075         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
47076         this.frame = Roo.get(this.getId() + '___Frame')
47077     },
47078     
47079     _getConfigHtml : function()
47080     {
47081         var sConfig = '' ;
47082
47083         for ( var o in this.fckconfig ) {
47084             sConfig += sConfig.length > 0  ? '&amp;' : '';
47085             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47086         }
47087
47088         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47089     },
47090     
47091     
47092     _getIFrameHtml : function()
47093     {
47094         var sFile = 'fckeditor.html' ;
47095         /* no idea what this is about..
47096         try
47097         {
47098             if ( (/fcksource=true/i).test( window.top.location.search ) )
47099                 sFile = 'fckeditor.original.html' ;
47100         }
47101         catch (e) { 
47102         */
47103
47104         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47105         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47106         
47107         
47108         var html = '<iframe id="' + this.getId() +
47109             '___Frame" src="' + sLink +
47110             '" width="' + this.width +
47111             '" height="' + this.height + '"' +
47112             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47113             ' frameborder="0" scrolling="no"></iframe>' ;
47114
47115         return html ;
47116     },
47117     
47118     _insertHtmlBefore : function( html, element )
47119     {
47120         if ( element.insertAdjacentHTML )       {
47121             // IE
47122             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47123         } else { // Gecko
47124             var oRange = document.createRange() ;
47125             oRange.setStartBefore( element ) ;
47126             var oFragment = oRange.createContextualFragment( html );
47127             element.parentNode.insertBefore( oFragment, element ) ;
47128         }
47129     }
47130     
47131     
47132   
47133     
47134     
47135     
47136     
47137
47138 });
47139
47140 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47141
47142 function FCKeditor_OnComplete(editorInstance){
47143     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47144     f.fckEditor = editorInstance;
47145     //console.log("loaded");
47146     f.fireEvent('editorinit', f, editorInstance);
47147
47148   
47149
47150  
47151
47152
47153
47154
47155
47156
47157
47158
47159
47160
47161
47162
47163
47164
47165
47166 //<script type="text/javascript">
47167 /**
47168  * @class Roo.form.GridField
47169  * @extends Roo.form.Field
47170  * Embed a grid (or editable grid into a form)
47171  * STATUS ALPHA
47172  * 
47173  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47174  * it needs 
47175  * xgrid.store = Roo.data.Store
47176  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47177  * xgrid.store.reader = Roo.data.JsonReader 
47178  * 
47179  * 
47180  * @constructor
47181  * Creates a new GridField
47182  * @param {Object} config Configuration options
47183  */
47184 Roo.form.GridField = function(config){
47185     Roo.form.GridField.superclass.constructor.call(this, config);
47186      
47187 };
47188
47189 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47190     /**
47191      * @cfg {Number} width  - used to restrict width of grid..
47192      */
47193     width : 100,
47194     /**
47195      * @cfg {Number} height - used to restrict height of grid..
47196      */
47197     height : 50,
47198      /**
47199      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47200          * 
47201          *}
47202      */
47203     xgrid : false, 
47204     /**
47205      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47206      * {tag: "input", type: "checkbox", autocomplete: "off"})
47207      */
47208    // defaultAutoCreate : { tag: 'div' },
47209     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47210     /**
47211      * @cfg {String} addTitle Text to include for adding a title.
47212      */
47213     addTitle : false,
47214     //
47215     onResize : function(){
47216         Roo.form.Field.superclass.onResize.apply(this, arguments);
47217     },
47218
47219     initEvents : function(){
47220         // Roo.form.Checkbox.superclass.initEvents.call(this);
47221         // has no events...
47222        
47223     },
47224
47225
47226     getResizeEl : function(){
47227         return this.wrap;
47228     },
47229
47230     getPositionEl : function(){
47231         return this.wrap;
47232     },
47233
47234     // private
47235     onRender : function(ct, position){
47236         
47237         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47238         var style = this.style;
47239         delete this.style;
47240         
47241         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47242         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47243         this.viewEl = this.wrap.createChild({ tag: 'div' });
47244         if (style) {
47245             this.viewEl.applyStyles(style);
47246         }
47247         if (this.width) {
47248             this.viewEl.setWidth(this.width);
47249         }
47250         if (this.height) {
47251             this.viewEl.setHeight(this.height);
47252         }
47253         //if(this.inputValue !== undefined){
47254         //this.setValue(this.value);
47255         
47256         
47257         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47258         
47259         
47260         this.grid.render();
47261         this.grid.getDataSource().on('remove', this.refreshValue, this);
47262         this.grid.getDataSource().on('update', this.refreshValue, this);
47263         this.grid.on('afteredit', this.refreshValue, this);
47264  
47265     },
47266      
47267     
47268     /**
47269      * Sets the value of the item. 
47270      * @param {String} either an object  or a string..
47271      */
47272     setValue : function(v){
47273         //this.value = v;
47274         v = v || []; // empty set..
47275         // this does not seem smart - it really only affects memoryproxy grids..
47276         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47277             var ds = this.grid.getDataSource();
47278             // assumes a json reader..
47279             var data = {}
47280             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47281             ds.loadData( data);
47282         }
47283         // clear selection so it does not get stale.
47284         if (this.grid.sm) { 
47285             this.grid.sm.clearSelections();
47286         }
47287         
47288         Roo.form.GridField.superclass.setValue.call(this, v);
47289         this.refreshValue();
47290         // should load data in the grid really....
47291     },
47292     
47293     // private
47294     refreshValue: function() {
47295          var val = [];
47296         this.grid.getDataSource().each(function(r) {
47297             val.push(r.data);
47298         });
47299         this.el.dom.value = Roo.encode(val);
47300     }
47301     
47302      
47303     
47304     
47305 });/*
47306  * Based on:
47307  * Ext JS Library 1.1.1
47308  * Copyright(c) 2006-2007, Ext JS, LLC.
47309  *
47310  * Originally Released Under LGPL - original licence link has changed is not relivant.
47311  *
47312  * Fork - LGPL
47313  * <script type="text/javascript">
47314  */
47315 /**
47316  * @class Roo.form.DisplayField
47317  * @extends Roo.form.Field
47318  * A generic Field to display non-editable data.
47319  * @cfg {Boolean} closable (true|false) default false
47320  * @constructor
47321  * Creates a new Display Field item.
47322  * @param {Object} config Configuration options
47323  */
47324 Roo.form.DisplayField = function(config){
47325     Roo.form.DisplayField.superclass.constructor.call(this, config);
47326     
47327     this.addEvents({
47328         /**
47329          * @event close
47330          * Fires after the click the close btn
47331              * @param {Roo.form.DisplayField} this
47332              */
47333         close : true
47334     });
47335 };
47336
47337 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47338     inputType:      'hidden',
47339     allowBlank:     true,
47340     readOnly:         true,
47341     
47342  
47343     /**
47344      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47345      */
47346     focusClass : undefined,
47347     /**
47348      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47349      */
47350     fieldClass: 'x-form-field',
47351     
47352      /**
47353      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47354      */
47355     valueRenderer: undefined,
47356     
47357     width: 100,
47358     /**
47359      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47360      * {tag: "input", type: "checkbox", autocomplete: "off"})
47361      */
47362      
47363  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47364  
47365     closable : false,
47366     
47367     onResize : function(){
47368         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47369         
47370     },
47371
47372     initEvents : function(){
47373         // Roo.form.Checkbox.superclass.initEvents.call(this);
47374         // has no events...
47375         
47376         if(this.closable){
47377             this.closeEl.on('click', this.onClose, this);
47378         }
47379        
47380     },
47381
47382
47383     getResizeEl : function(){
47384         return this.wrap;
47385     },
47386
47387     getPositionEl : function(){
47388         return this.wrap;
47389     },
47390
47391     // private
47392     onRender : function(ct, position){
47393         
47394         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47395         //if(this.inputValue !== undefined){
47396         this.wrap = this.el.wrap();
47397         
47398         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47399         
47400         if(this.closable){
47401             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
47402         }
47403         
47404         if (this.bodyStyle) {
47405             this.viewEl.applyStyles(this.bodyStyle);
47406         }
47407         //this.viewEl.setStyle('padding', '2px');
47408         
47409         this.setValue(this.value);
47410         
47411     },
47412 /*
47413     // private
47414     initValue : Roo.emptyFn,
47415
47416   */
47417
47418         // private
47419     onClick : function(){
47420         
47421     },
47422
47423     /**
47424      * Sets the checked state of the checkbox.
47425      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47426      */
47427     setValue : function(v){
47428         this.value = v;
47429         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47430         // this might be called before we have a dom element..
47431         if (!this.viewEl) {
47432             return;
47433         }
47434         this.viewEl.dom.innerHTML = html;
47435         Roo.form.DisplayField.superclass.setValue.call(this, v);
47436
47437     },
47438     
47439     onClose : function(e)
47440     {
47441         e.preventDefault();
47442         
47443         this.fireEvent('close', this);
47444     }
47445 });/*
47446  * 
47447  * Licence- LGPL
47448  * 
47449  */
47450
47451 /**
47452  * @class Roo.form.DayPicker
47453  * @extends Roo.form.Field
47454  * A Day picker show [M] [T] [W] ....
47455  * @constructor
47456  * Creates a new Day Picker
47457  * @param {Object} config Configuration options
47458  */
47459 Roo.form.DayPicker= function(config){
47460     Roo.form.DayPicker.superclass.constructor.call(this, config);
47461      
47462 };
47463
47464 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47465     /**
47466      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47467      */
47468     focusClass : undefined,
47469     /**
47470      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47471      */
47472     fieldClass: "x-form-field",
47473    
47474     /**
47475      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47476      * {tag: "input", type: "checkbox", autocomplete: "off"})
47477      */
47478     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47479     
47480    
47481     actionMode : 'viewEl', 
47482     //
47483     // private
47484  
47485     inputType : 'hidden',
47486     
47487      
47488     inputElement: false, // real input element?
47489     basedOn: false, // ????
47490     
47491     isFormField: true, // not sure where this is needed!!!!
47492
47493     onResize : function(){
47494         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47495         if(!this.boxLabel){
47496             this.el.alignTo(this.wrap, 'c-c');
47497         }
47498     },
47499
47500     initEvents : function(){
47501         Roo.form.Checkbox.superclass.initEvents.call(this);
47502         this.el.on("click", this.onClick,  this);
47503         this.el.on("change", this.onClick,  this);
47504     },
47505
47506
47507     getResizeEl : function(){
47508         return this.wrap;
47509     },
47510
47511     getPositionEl : function(){
47512         return this.wrap;
47513     },
47514
47515     
47516     // private
47517     onRender : function(ct, position){
47518         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47519        
47520         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47521         
47522         var r1 = '<table><tr>';
47523         var r2 = '<tr class="x-form-daypick-icons">';
47524         for (var i=0; i < 7; i++) {
47525             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47526             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47527         }
47528         
47529         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47530         viewEl.select('img').on('click', this.onClick, this);
47531         this.viewEl = viewEl;   
47532         
47533         
47534         // this will not work on Chrome!!!
47535         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47536         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47537         
47538         
47539           
47540
47541     },
47542
47543     // private
47544     initValue : Roo.emptyFn,
47545
47546     /**
47547      * Returns the checked state of the checkbox.
47548      * @return {Boolean} True if checked, else false
47549      */
47550     getValue : function(){
47551         return this.el.dom.value;
47552         
47553     },
47554
47555         // private
47556     onClick : function(e){ 
47557         //this.setChecked(!this.checked);
47558         Roo.get(e.target).toggleClass('x-menu-item-checked');
47559         this.refreshValue();
47560         //if(this.el.dom.checked != this.checked){
47561         //    this.setValue(this.el.dom.checked);
47562        // }
47563     },
47564     
47565     // private
47566     refreshValue : function()
47567     {
47568         var val = '';
47569         this.viewEl.select('img',true).each(function(e,i,n)  {
47570             val += e.is(".x-menu-item-checked") ? String(n) : '';
47571         });
47572         this.setValue(val, true);
47573     },
47574
47575     /**
47576      * Sets the checked state of the checkbox.
47577      * On is always based on a string comparison between inputValue and the param.
47578      * @param {Boolean/String} value - the value to set 
47579      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47580      */
47581     setValue : function(v,suppressEvent){
47582         if (!this.el.dom) {
47583             return;
47584         }
47585         var old = this.el.dom.value ;
47586         this.el.dom.value = v;
47587         if (suppressEvent) {
47588             return ;
47589         }
47590          
47591         // update display..
47592         this.viewEl.select('img',true).each(function(e,i,n)  {
47593             
47594             var on = e.is(".x-menu-item-checked");
47595             var newv = v.indexOf(String(n)) > -1;
47596             if (on != newv) {
47597                 e.toggleClass('x-menu-item-checked');
47598             }
47599             
47600         });
47601         
47602         
47603         this.fireEvent('change', this, v, old);
47604         
47605         
47606     },
47607    
47608     // handle setting of hidden value by some other method!!?!?
47609     setFromHidden: function()
47610     {
47611         if(!this.el){
47612             return;
47613         }
47614         //console.log("SET FROM HIDDEN");
47615         //alert('setFrom hidden');
47616         this.setValue(this.el.dom.value);
47617     },
47618     
47619     onDestroy : function()
47620     {
47621         if(this.viewEl){
47622             Roo.get(this.viewEl).remove();
47623         }
47624          
47625         Roo.form.DayPicker.superclass.onDestroy.call(this);
47626     }
47627
47628 });/*
47629  * RooJS Library 1.1.1
47630  * Copyright(c) 2008-2011  Alan Knowles
47631  *
47632  * License - LGPL
47633  */
47634  
47635
47636 /**
47637  * @class Roo.form.ComboCheck
47638  * @extends Roo.form.ComboBox
47639  * A combobox for multiple select items.
47640  *
47641  * FIXME - could do with a reset button..
47642  * 
47643  * @constructor
47644  * Create a new ComboCheck
47645  * @param {Object} config Configuration options
47646  */
47647 Roo.form.ComboCheck = function(config){
47648     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47649     // should verify some data...
47650     // like
47651     // hiddenName = required..
47652     // displayField = required
47653     // valudField == required
47654     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47655     var _t = this;
47656     Roo.each(req, function(e) {
47657         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47658             throw "Roo.form.ComboCheck : missing value for: " + e;
47659         }
47660     });
47661     
47662     
47663 };
47664
47665 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47666      
47667      
47668     editable : false,
47669      
47670     selectedClass: 'x-menu-item-checked', 
47671     
47672     // private
47673     onRender : function(ct, position){
47674         var _t = this;
47675         
47676         
47677         
47678         if(!this.tpl){
47679             var cls = 'x-combo-list';
47680
47681             
47682             this.tpl =  new Roo.Template({
47683                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47684                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47685                    '<span>{' + this.displayField + '}</span>' +
47686                     '</div>' 
47687                 
47688             });
47689         }
47690  
47691         
47692         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47693         this.view.singleSelect = false;
47694         this.view.multiSelect = true;
47695         this.view.toggleSelect = true;
47696         this.pageTb.add(new Roo.Toolbar.Fill(), {
47697             
47698             text: 'Done',
47699             handler: function()
47700             {
47701                 _t.collapse();
47702             }
47703         });
47704     },
47705     
47706     onViewOver : function(e, t){
47707         // do nothing...
47708         return;
47709         
47710     },
47711     
47712     onViewClick : function(doFocus,index){
47713         return;
47714         
47715     },
47716     select: function () {
47717         //Roo.log("SELECT CALLED");
47718     },
47719      
47720     selectByValue : function(xv, scrollIntoView){
47721         var ar = this.getValueArray();
47722         var sels = [];
47723         
47724         Roo.each(ar, function(v) {
47725             if(v === undefined || v === null){
47726                 return;
47727             }
47728             var r = this.findRecord(this.valueField, v);
47729             if(r){
47730                 sels.push(this.store.indexOf(r))
47731                 
47732             }
47733         },this);
47734         this.view.select(sels);
47735         return false;
47736     },
47737     
47738     
47739     
47740     onSelect : function(record, index){
47741        // Roo.log("onselect Called");
47742        // this is only called by the clear button now..
47743         this.view.clearSelections();
47744         this.setValue('[]');
47745         if (this.value != this.valueBefore) {
47746             this.fireEvent('change', this, this.value, this.valueBefore);
47747             this.valueBefore = this.value;
47748         }
47749     },
47750     getValueArray : function()
47751     {
47752         var ar = [] ;
47753         
47754         try {
47755             //Roo.log(this.value);
47756             if (typeof(this.value) == 'undefined') {
47757                 return [];
47758             }
47759             var ar = Roo.decode(this.value);
47760             return  ar instanceof Array ? ar : []; //?? valid?
47761             
47762         } catch(e) {
47763             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47764             return [];
47765         }
47766          
47767     },
47768     expand : function ()
47769     {
47770         
47771         Roo.form.ComboCheck.superclass.expand.call(this);
47772         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47773         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47774         
47775
47776     },
47777     
47778     collapse : function(){
47779         Roo.form.ComboCheck.superclass.collapse.call(this);
47780         var sl = this.view.getSelectedIndexes();
47781         var st = this.store;
47782         var nv = [];
47783         var tv = [];
47784         var r;
47785         Roo.each(sl, function(i) {
47786             r = st.getAt(i);
47787             nv.push(r.get(this.valueField));
47788         },this);
47789         this.setValue(Roo.encode(nv));
47790         if (this.value != this.valueBefore) {
47791
47792             this.fireEvent('change', this, this.value, this.valueBefore);
47793             this.valueBefore = this.value;
47794         }
47795         
47796     },
47797     
47798     setValue : function(v){
47799         // Roo.log(v);
47800         this.value = v;
47801         
47802         var vals = this.getValueArray();
47803         var tv = [];
47804         Roo.each(vals, function(k) {
47805             var r = this.findRecord(this.valueField, k);
47806             if(r){
47807                 tv.push(r.data[this.displayField]);
47808             }else if(this.valueNotFoundText !== undefined){
47809                 tv.push( this.valueNotFoundText );
47810             }
47811         },this);
47812        // Roo.log(tv);
47813         
47814         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47815         this.hiddenField.value = v;
47816         this.value = v;
47817     }
47818     
47819 });/*
47820  * Based on:
47821  * Ext JS Library 1.1.1
47822  * Copyright(c) 2006-2007, Ext JS, LLC.
47823  *
47824  * Originally Released Under LGPL - original licence link has changed is not relivant.
47825  *
47826  * Fork - LGPL
47827  * <script type="text/javascript">
47828  */
47829  
47830 /**
47831  * @class Roo.form.Signature
47832  * @extends Roo.form.Field
47833  * Signature field.  
47834  * @constructor
47835  * 
47836  * @param {Object} config Configuration options
47837  */
47838
47839 Roo.form.Signature = function(config){
47840     Roo.form.Signature.superclass.constructor.call(this, config);
47841     
47842     this.addEvents({// not in used??
47843          /**
47844          * @event confirm
47845          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47846              * @param {Roo.form.Signature} combo This combo box
47847              */
47848         'confirm' : true,
47849         /**
47850          * @event reset
47851          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47852              * @param {Roo.form.ComboBox} combo This combo box
47853              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47854              */
47855         'reset' : true
47856     });
47857 };
47858
47859 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47860     /**
47861      * @cfg {Object} labels Label to use when rendering a form.
47862      * defaults to 
47863      * labels : { 
47864      *      clear : "Clear",
47865      *      confirm : "Confirm"
47866      *  }
47867      */
47868     labels : { 
47869         clear : "Clear",
47870         confirm : "Confirm"
47871     },
47872     /**
47873      * @cfg {Number} width The signature panel width (defaults to 300)
47874      */
47875     width: 300,
47876     /**
47877      * @cfg {Number} height The signature panel height (defaults to 100)
47878      */
47879     height : 100,
47880     /**
47881      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47882      */
47883     allowBlank : false,
47884     
47885     //private
47886     // {Object} signPanel The signature SVG panel element (defaults to {})
47887     signPanel : {},
47888     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47889     isMouseDown : false,
47890     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47891     isConfirmed : false,
47892     // {String} signatureTmp SVG mapping string (defaults to empty string)
47893     signatureTmp : '',
47894     
47895     
47896     defaultAutoCreate : { // modified by initCompnoent..
47897         tag: "input",
47898         type:"hidden"
47899     },
47900
47901     // private
47902     onRender : function(ct, position){
47903         
47904         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47905         
47906         this.wrap = this.el.wrap({
47907             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47908         });
47909         
47910         this.createToolbar(this);
47911         this.signPanel = this.wrap.createChild({
47912                 tag: 'div',
47913                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47914             }, this.el
47915         );
47916             
47917         this.svgID = Roo.id();
47918         this.svgEl = this.signPanel.createChild({
47919               xmlns : 'http://www.w3.org/2000/svg',
47920               tag : 'svg',
47921               id : this.svgID + "-svg",
47922               width: this.width,
47923               height: this.height,
47924               viewBox: '0 0 '+this.width+' '+this.height,
47925               cn : [
47926                 {
47927                     tag: "rect",
47928                     id: this.svgID + "-svg-r",
47929                     width: this.width,
47930                     height: this.height,
47931                     fill: "#ffa"
47932                 },
47933                 {
47934                     tag: "line",
47935                     id: this.svgID + "-svg-l",
47936                     x1: "0", // start
47937                     y1: (this.height*0.8), // start set the line in 80% of height
47938                     x2: this.width, // end
47939                     y2: (this.height*0.8), // end set the line in 80% of height
47940                     'stroke': "#666",
47941                     'stroke-width': "1",
47942                     'stroke-dasharray': "3",
47943                     'shape-rendering': "crispEdges",
47944                     'pointer-events': "none"
47945                 },
47946                 {
47947                     tag: "path",
47948                     id: this.svgID + "-svg-p",
47949                     'stroke': "navy",
47950                     'stroke-width': "3",
47951                     'fill': "none",
47952                     'pointer-events': 'none'
47953                 }
47954               ]
47955         });
47956         this.createSVG();
47957         this.svgBox = this.svgEl.dom.getScreenCTM();
47958     },
47959     createSVG : function(){ 
47960         var svg = this.signPanel;
47961         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47962         var t = this;
47963
47964         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47965         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47966         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47967         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47968         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47969         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47970         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47971         
47972     },
47973     isTouchEvent : function(e){
47974         return e.type.match(/^touch/);
47975     },
47976     getCoords : function (e) {
47977         var pt    = this.svgEl.dom.createSVGPoint();
47978         pt.x = e.clientX; 
47979         pt.y = e.clientY;
47980         if (this.isTouchEvent(e)) {
47981             pt.x =  e.targetTouches[0].clientX;
47982             pt.y = e.targetTouches[0].clientY;
47983         }
47984         var a = this.svgEl.dom.getScreenCTM();
47985         var b = a.inverse();
47986         var mx = pt.matrixTransform(b);
47987         return mx.x + ',' + mx.y;
47988     },
47989     //mouse event headler 
47990     down : function (e) {
47991         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47992         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47993         
47994         this.isMouseDown = true;
47995         
47996         e.preventDefault();
47997     },
47998     move : function (e) {
47999         if (this.isMouseDown) {
48000             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
48001             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
48002         }
48003         
48004         e.preventDefault();
48005     },
48006     up : function (e) {
48007         this.isMouseDown = false;
48008         var sp = this.signatureTmp.split(' ');
48009         
48010         if(sp.length > 1){
48011             if(!sp[sp.length-2].match(/^L/)){
48012                 sp.pop();
48013                 sp.pop();
48014                 sp.push("");
48015                 this.signatureTmp = sp.join(" ");
48016             }
48017         }
48018         if(this.getValue() != this.signatureTmp){
48019             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48020             this.isConfirmed = false;
48021         }
48022         e.preventDefault();
48023     },
48024     
48025     /**
48026      * Protected method that will not generally be called directly. It
48027      * is called when the editor creates its toolbar. Override this method if you need to
48028      * add custom toolbar buttons.
48029      * @param {HtmlEditor} editor
48030      */
48031     createToolbar : function(editor){
48032          function btn(id, toggle, handler){
48033             var xid = fid + '-'+ id ;
48034             return {
48035                 id : xid,
48036                 cmd : id,
48037                 cls : 'x-btn-icon x-edit-'+id,
48038                 enableToggle:toggle !== false,
48039                 scope: editor, // was editor...
48040                 handler:handler||editor.relayBtnCmd,
48041                 clickEvent:'mousedown',
48042                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48043                 tabIndex:-1
48044             };
48045         }
48046         
48047         
48048         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48049         this.tb = tb;
48050         this.tb.add(
48051            {
48052                 cls : ' x-signature-btn x-signature-'+id,
48053                 scope: editor, // was editor...
48054                 handler: this.reset,
48055                 clickEvent:'mousedown',
48056                 text: this.labels.clear
48057             },
48058             {
48059                  xtype : 'Fill',
48060                  xns: Roo.Toolbar
48061             }, 
48062             {
48063                 cls : '  x-signature-btn x-signature-'+id,
48064                 scope: editor, // was editor...
48065                 handler: this.confirmHandler,
48066                 clickEvent:'mousedown',
48067                 text: this.labels.confirm
48068             }
48069         );
48070     
48071     },
48072     //public
48073     /**
48074      * when user is clicked confirm then show this image.....
48075      * 
48076      * @return {String} Image Data URI
48077      */
48078     getImageDataURI : function(){
48079         var svg = this.svgEl.dom.parentNode.innerHTML;
48080         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
48081         return src; 
48082     },
48083     /**
48084      * 
48085      * @return {Boolean} this.isConfirmed
48086      */
48087     getConfirmed : function(){
48088         return this.isConfirmed;
48089     },
48090     /**
48091      * 
48092      * @return {Number} this.width
48093      */
48094     getWidth : function(){
48095         return this.width;
48096     },
48097     /**
48098      * 
48099      * @return {Number} this.height
48100      */
48101     getHeight : function(){
48102         return this.height;
48103     },
48104     // private
48105     getSignature : function(){
48106         return this.signatureTmp;
48107     },
48108     // private
48109     reset : function(){
48110         this.signatureTmp = '';
48111         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48112         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48113         this.isConfirmed = false;
48114         Roo.form.Signature.superclass.reset.call(this);
48115     },
48116     setSignature : function(s){
48117         this.signatureTmp = s;
48118         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48119         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48120         this.setValue(s);
48121         this.isConfirmed = false;
48122         Roo.form.Signature.superclass.reset.call(this);
48123     }, 
48124     test : function(){
48125 //        Roo.log(this.signPanel.dom.contentWindow.up())
48126     },
48127     //private
48128     setConfirmed : function(){
48129         
48130         
48131         
48132 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48133     },
48134     // private
48135     confirmHandler : function(){
48136         if(!this.getSignature()){
48137             return;
48138         }
48139         
48140         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48141         this.setValue(this.getSignature());
48142         this.isConfirmed = true;
48143         
48144         this.fireEvent('confirm', this);
48145     },
48146     // private
48147     // Subclasses should provide the validation implementation by overriding this
48148     validateValue : function(value){
48149         if(this.allowBlank){
48150             return true;
48151         }
48152         
48153         if(this.isConfirmed){
48154             return true;
48155         }
48156         return false;
48157     }
48158 });/*
48159  * Based on:
48160  * Ext JS Library 1.1.1
48161  * Copyright(c) 2006-2007, Ext JS, LLC.
48162  *
48163  * Originally Released Under LGPL - original licence link has changed is not relivant.
48164  *
48165  * Fork - LGPL
48166  * <script type="text/javascript">
48167  */
48168  
48169
48170 /**
48171  * @class Roo.form.ComboBox
48172  * @extends Roo.form.TriggerField
48173  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48174  * @constructor
48175  * Create a new ComboBox.
48176  * @param {Object} config Configuration options
48177  */
48178 Roo.form.Select = function(config){
48179     Roo.form.Select.superclass.constructor.call(this, config);
48180      
48181 };
48182
48183 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48184     /**
48185      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48186      */
48187     /**
48188      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48189      * rendering into an Roo.Editor, defaults to false)
48190      */
48191     /**
48192      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48193      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48194      */
48195     /**
48196      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48197      */
48198     /**
48199      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48200      * the dropdown list (defaults to undefined, with no header element)
48201      */
48202
48203      /**
48204      * @cfg {String/Roo.Template} tpl The template to use to render the output
48205      */
48206      
48207     // private
48208     defaultAutoCreate : {tag: "select"  },
48209     /**
48210      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48211      */
48212     listWidth: undefined,
48213     /**
48214      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48215      * mode = 'remote' or 'text' if mode = 'local')
48216      */
48217     displayField: undefined,
48218     /**
48219      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48220      * mode = 'remote' or 'value' if mode = 'local'). 
48221      * Note: use of a valueField requires the user make a selection
48222      * in order for a value to be mapped.
48223      */
48224     valueField: undefined,
48225     
48226     
48227     /**
48228      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48229      * field's data value (defaults to the underlying DOM element's name)
48230      */
48231     hiddenName: undefined,
48232     /**
48233      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48234      */
48235     listClass: '',
48236     /**
48237      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48238      */
48239     selectedClass: 'x-combo-selected',
48240     /**
48241      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48242      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48243      * which displays a downward arrow icon).
48244      */
48245     triggerClass : 'x-form-arrow-trigger',
48246     /**
48247      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48248      */
48249     shadow:'sides',
48250     /**
48251      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48252      * anchor positions (defaults to 'tl-bl')
48253      */
48254     listAlign: 'tl-bl?',
48255     /**
48256      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48257      */
48258     maxHeight: 300,
48259     /**
48260      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48261      * query specified by the allQuery config option (defaults to 'query')
48262      */
48263     triggerAction: 'query',
48264     /**
48265      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48266      * (defaults to 4, does not apply if editable = false)
48267      */
48268     minChars : 4,
48269     /**
48270      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48271      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48272      */
48273     typeAhead: false,
48274     /**
48275      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48276      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48277      */
48278     queryDelay: 500,
48279     /**
48280      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48281      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48282      */
48283     pageSize: 0,
48284     /**
48285      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48286      * when editable = true (defaults to false)
48287      */
48288     selectOnFocus:false,
48289     /**
48290      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48291      */
48292     queryParam: 'query',
48293     /**
48294      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48295      * when mode = 'remote' (defaults to 'Loading...')
48296      */
48297     loadingText: 'Loading...',
48298     /**
48299      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48300      */
48301     resizable: false,
48302     /**
48303      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48304      */
48305     handleHeight : 8,
48306     /**
48307      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48308      * traditional select (defaults to true)
48309      */
48310     editable: true,
48311     /**
48312      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48313      */
48314     allQuery: '',
48315     /**
48316      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48317      */
48318     mode: 'remote',
48319     /**
48320      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48321      * listWidth has a higher value)
48322      */
48323     minListWidth : 70,
48324     /**
48325      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48326      * allow the user to set arbitrary text into the field (defaults to false)
48327      */
48328     forceSelection:false,
48329     /**
48330      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48331      * if typeAhead = true (defaults to 250)
48332      */
48333     typeAheadDelay : 250,
48334     /**
48335      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48336      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48337      */
48338     valueNotFoundText : undefined,
48339     
48340     /**
48341      * @cfg {String} defaultValue The value displayed after loading the store.
48342      */
48343     defaultValue: '',
48344     
48345     /**
48346      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48347      */
48348     blockFocus : false,
48349     
48350     /**
48351      * @cfg {Boolean} disableClear Disable showing of clear button.
48352      */
48353     disableClear : false,
48354     /**
48355      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48356      */
48357     alwaysQuery : false,
48358     
48359     //private
48360     addicon : false,
48361     editicon: false,
48362     
48363     // element that contains real text value.. (when hidden is used..)
48364      
48365     // private
48366     onRender : function(ct, position){
48367         Roo.form.Field.prototype.onRender.call(this, ct, position);
48368         
48369         if(this.store){
48370             this.store.on('beforeload', this.onBeforeLoad, this);
48371             this.store.on('load', this.onLoad, this);
48372             this.store.on('loadexception', this.onLoadException, this);
48373             this.store.load({});
48374         }
48375         
48376         
48377         
48378     },
48379
48380     // private
48381     initEvents : function(){
48382         //Roo.form.ComboBox.superclass.initEvents.call(this);
48383  
48384     },
48385
48386     onDestroy : function(){
48387        
48388         if(this.store){
48389             this.store.un('beforeload', this.onBeforeLoad, this);
48390             this.store.un('load', this.onLoad, this);
48391             this.store.un('loadexception', this.onLoadException, this);
48392         }
48393         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48394     },
48395
48396     // private
48397     fireKey : function(e){
48398         if(e.isNavKeyPress() && !this.list.isVisible()){
48399             this.fireEvent("specialkey", this, e);
48400         }
48401     },
48402
48403     // private
48404     onResize: function(w, h){
48405         
48406         return; 
48407     
48408         
48409     },
48410
48411     /**
48412      * Allow or prevent the user from directly editing the field text.  If false is passed,
48413      * the user will only be able to select from the items defined in the dropdown list.  This method
48414      * is the runtime equivalent of setting the 'editable' config option at config time.
48415      * @param {Boolean} value True to allow the user to directly edit the field text
48416      */
48417     setEditable : function(value){
48418          
48419     },
48420
48421     // private
48422     onBeforeLoad : function(){
48423         
48424         Roo.log("Select before load");
48425         return;
48426     
48427         this.innerList.update(this.loadingText ?
48428                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48429         //this.restrictHeight();
48430         this.selectedIndex = -1;
48431     },
48432
48433     // private
48434     onLoad : function(){
48435
48436     
48437         var dom = this.el.dom;
48438         dom.innerHTML = '';
48439          var od = dom.ownerDocument;
48440          
48441         if (this.emptyText) {
48442             var op = od.createElement('option');
48443             op.setAttribute('value', '');
48444             op.innerHTML = String.format('{0}', this.emptyText);
48445             dom.appendChild(op);
48446         }
48447         if(this.store.getCount() > 0){
48448            
48449             var vf = this.valueField;
48450             var df = this.displayField;
48451             this.store.data.each(function(r) {
48452                 // which colmsn to use... testing - cdoe / title..
48453                 var op = od.createElement('option');
48454                 op.setAttribute('value', r.data[vf]);
48455                 op.innerHTML = String.format('{0}', r.data[df]);
48456                 dom.appendChild(op);
48457             });
48458             if (typeof(this.defaultValue != 'undefined')) {
48459                 this.setValue(this.defaultValue);
48460             }
48461             
48462              
48463         }else{
48464             //this.onEmptyResults();
48465         }
48466         //this.el.focus();
48467     },
48468     // private
48469     onLoadException : function()
48470     {
48471         dom.innerHTML = '';
48472             
48473         Roo.log("Select on load exception");
48474         return;
48475     
48476         this.collapse();
48477         Roo.log(this.store.reader.jsonData);
48478         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48479             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48480         }
48481         
48482         
48483     },
48484     // private
48485     onTypeAhead : function(){
48486          
48487     },
48488
48489     // private
48490     onSelect : function(record, index){
48491         Roo.log('on select?');
48492         return;
48493         if(this.fireEvent('beforeselect', this, record, index) !== false){
48494             this.setFromData(index > -1 ? record.data : false);
48495             this.collapse();
48496             this.fireEvent('select', this, record, index);
48497         }
48498     },
48499
48500     /**
48501      * Returns the currently selected field value or empty string if no value is set.
48502      * @return {String} value The selected value
48503      */
48504     getValue : function(){
48505         var dom = this.el.dom;
48506         this.value = dom.options[dom.selectedIndex].value;
48507         return this.value;
48508         
48509     },
48510
48511     /**
48512      * Clears any text/value currently set in the field
48513      */
48514     clearValue : function(){
48515         this.value = '';
48516         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48517         
48518     },
48519
48520     /**
48521      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48522      * will be displayed in the field.  If the value does not match the data value of an existing item,
48523      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48524      * Otherwise the field will be blank (although the value will still be set).
48525      * @param {String} value The value to match
48526      */
48527     setValue : function(v){
48528         var d = this.el.dom;
48529         for (var i =0; i < d.options.length;i++) {
48530             if (v == d.options[i].value) {
48531                 d.selectedIndex = i;
48532                 this.value = v;
48533                 return;
48534             }
48535         }
48536         this.clearValue();
48537     },
48538     /**
48539      * @property {Object} the last set data for the element
48540      */
48541     
48542     lastData : false,
48543     /**
48544      * Sets the value of the field based on a object which is related to the record format for the store.
48545      * @param {Object} value the value to set as. or false on reset?
48546      */
48547     setFromData : function(o){
48548         Roo.log('setfrom data?');
48549          
48550         
48551         
48552     },
48553     // private
48554     reset : function(){
48555         this.clearValue();
48556     },
48557     // private
48558     findRecord : function(prop, value){
48559         
48560         return false;
48561     
48562         var record;
48563         if(this.store.getCount() > 0){
48564             this.store.each(function(r){
48565                 if(r.data[prop] == value){
48566                     record = r;
48567                     return false;
48568                 }
48569                 return true;
48570             });
48571         }
48572         return record;
48573     },
48574     
48575     getName: function()
48576     {
48577         // returns hidden if it's set..
48578         if (!this.rendered) {return ''};
48579         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48580         
48581     },
48582      
48583
48584     
48585
48586     // private
48587     onEmptyResults : function(){
48588         Roo.log('empty results');
48589         //this.collapse();
48590     },
48591
48592     /**
48593      * Returns true if the dropdown list is expanded, else false.
48594      */
48595     isExpanded : function(){
48596         return false;
48597     },
48598
48599     /**
48600      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48601      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48602      * @param {String} value The data value of the item to select
48603      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48604      * selected item if it is not currently in view (defaults to true)
48605      * @return {Boolean} True if the value matched an item in the list, else false
48606      */
48607     selectByValue : function(v, scrollIntoView){
48608         Roo.log('select By Value');
48609         return false;
48610     
48611         if(v !== undefined && v !== null){
48612             var r = this.findRecord(this.valueField || this.displayField, v);
48613             if(r){
48614                 this.select(this.store.indexOf(r), scrollIntoView);
48615                 return true;
48616             }
48617         }
48618         return false;
48619     },
48620
48621     /**
48622      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48623      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48624      * @param {Number} index The zero-based index of the list item to select
48625      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48626      * selected item if it is not currently in view (defaults to true)
48627      */
48628     select : function(index, scrollIntoView){
48629         Roo.log('select ');
48630         return  ;
48631         
48632         this.selectedIndex = index;
48633         this.view.select(index);
48634         if(scrollIntoView !== false){
48635             var el = this.view.getNode(index);
48636             if(el){
48637                 this.innerList.scrollChildIntoView(el, false);
48638             }
48639         }
48640     },
48641
48642       
48643
48644     // private
48645     validateBlur : function(){
48646         
48647         return;
48648         
48649     },
48650
48651     // private
48652     initQuery : function(){
48653         this.doQuery(this.getRawValue());
48654     },
48655
48656     // private
48657     doForce : function(){
48658         if(this.el.dom.value.length > 0){
48659             this.el.dom.value =
48660                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48661              
48662         }
48663     },
48664
48665     /**
48666      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48667      * query allowing the query action to be canceled if needed.
48668      * @param {String} query The SQL query to execute
48669      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48670      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48671      * saved in the current store (defaults to false)
48672      */
48673     doQuery : function(q, forceAll){
48674         
48675         Roo.log('doQuery?');
48676         if(q === undefined || q === null){
48677             q = '';
48678         }
48679         var qe = {
48680             query: q,
48681             forceAll: forceAll,
48682             combo: this,
48683             cancel:false
48684         };
48685         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48686             return false;
48687         }
48688         q = qe.query;
48689         forceAll = qe.forceAll;
48690         if(forceAll === true || (q.length >= this.minChars)){
48691             if(this.lastQuery != q || this.alwaysQuery){
48692                 this.lastQuery = q;
48693                 if(this.mode == 'local'){
48694                     this.selectedIndex = -1;
48695                     if(forceAll){
48696                         this.store.clearFilter();
48697                     }else{
48698                         this.store.filter(this.displayField, q);
48699                     }
48700                     this.onLoad();
48701                 }else{
48702                     this.store.baseParams[this.queryParam] = q;
48703                     this.store.load({
48704                         params: this.getParams(q)
48705                     });
48706                     this.expand();
48707                 }
48708             }else{
48709                 this.selectedIndex = -1;
48710                 this.onLoad();   
48711             }
48712         }
48713     },
48714
48715     // private
48716     getParams : function(q){
48717         var p = {};
48718         //p[this.queryParam] = q;
48719         if(this.pageSize){
48720             p.start = 0;
48721             p.limit = this.pageSize;
48722         }
48723         return p;
48724     },
48725
48726     /**
48727      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48728      */
48729     collapse : function(){
48730         
48731     },
48732
48733     // private
48734     collapseIf : function(e){
48735         
48736     },
48737
48738     /**
48739      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48740      */
48741     expand : function(){
48742         
48743     } ,
48744
48745     // private
48746      
48747
48748     /** 
48749     * @cfg {Boolean} grow 
48750     * @hide 
48751     */
48752     /** 
48753     * @cfg {Number} growMin 
48754     * @hide 
48755     */
48756     /** 
48757     * @cfg {Number} growMax 
48758     * @hide 
48759     */
48760     /**
48761      * @hide
48762      * @method autoSize
48763      */
48764     
48765     setWidth : function()
48766     {
48767         
48768     },
48769     getResizeEl : function(){
48770         return this.el;
48771     }
48772 });//<script type="text/javasscript">
48773  
48774
48775 /**
48776  * @class Roo.DDView
48777  * A DnD enabled version of Roo.View.
48778  * @param {Element/String} container The Element in which to create the View.
48779  * @param {String} tpl The template string used to create the markup for each element of the View
48780  * @param {Object} config The configuration properties. These include all the config options of
48781  * {@link Roo.View} plus some specific to this class.<br>
48782  * <p>
48783  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48784  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48785  * <p>
48786  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48787 .x-view-drag-insert-above {
48788         border-top:1px dotted #3366cc;
48789 }
48790 .x-view-drag-insert-below {
48791         border-bottom:1px dotted #3366cc;
48792 }
48793 </code></pre>
48794  * 
48795  */
48796  
48797 Roo.DDView = function(container, tpl, config) {
48798     Roo.DDView.superclass.constructor.apply(this, arguments);
48799     this.getEl().setStyle("outline", "0px none");
48800     this.getEl().unselectable();
48801     if (this.dragGroup) {
48802                 this.setDraggable(this.dragGroup.split(","));
48803     }
48804     if (this.dropGroup) {
48805                 this.setDroppable(this.dropGroup.split(","));
48806     }
48807     if (this.deletable) {
48808         this.setDeletable();
48809     }
48810     this.isDirtyFlag = false;
48811         this.addEvents({
48812                 "drop" : true
48813         });
48814 };
48815
48816 Roo.extend(Roo.DDView, Roo.View, {
48817 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48818 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48819 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48820 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48821
48822         isFormField: true,
48823
48824         reset: Roo.emptyFn,
48825         
48826         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48827
48828         validate: function() {
48829                 return true;
48830         },
48831         
48832         destroy: function() {
48833                 this.purgeListeners();
48834                 this.getEl.removeAllListeners();
48835                 this.getEl().remove();
48836                 if (this.dragZone) {
48837                         if (this.dragZone.destroy) {
48838                                 this.dragZone.destroy();
48839                         }
48840                 }
48841                 if (this.dropZone) {
48842                         if (this.dropZone.destroy) {
48843                                 this.dropZone.destroy();
48844                         }
48845                 }
48846         },
48847
48848 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48849         getName: function() {
48850                 return this.name;
48851         },
48852
48853 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48854         setValue: function(v) {
48855                 if (!this.store) {
48856                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48857                 }
48858                 var data = {};
48859                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48860                 this.store.proxy = new Roo.data.MemoryProxy(data);
48861                 this.store.load();
48862         },
48863
48864 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48865         getValue: function() {
48866                 var result = '(';
48867                 this.store.each(function(rec) {
48868                         result += rec.id + ',';
48869                 });
48870                 return result.substr(0, result.length - 1) + ')';
48871         },
48872         
48873         getIds: function() {
48874                 var i = 0, result = new Array(this.store.getCount());
48875                 this.store.each(function(rec) {
48876                         result[i++] = rec.id;
48877                 });
48878                 return result;
48879         },
48880         
48881         isDirty: function() {
48882                 return this.isDirtyFlag;
48883         },
48884
48885 /**
48886  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48887  *      whole Element becomes the target, and this causes the drop gesture to append.
48888  */
48889     getTargetFromEvent : function(e) {
48890                 var target = e.getTarget();
48891                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48892                 target = target.parentNode;
48893                 }
48894                 if (!target) {
48895                         target = this.el.dom.lastChild || this.el.dom;
48896                 }
48897                 return target;
48898     },
48899
48900 /**
48901  *      Create the drag data which consists of an object which has the property "ddel" as
48902  *      the drag proxy element. 
48903  */
48904     getDragData : function(e) {
48905         var target = this.findItemFromChild(e.getTarget());
48906                 if(target) {
48907                         this.handleSelection(e);
48908                         var selNodes = this.getSelectedNodes();
48909             var dragData = {
48910                 source: this,
48911                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48912                 nodes: selNodes,
48913                 records: []
48914                         };
48915                         var selectedIndices = this.getSelectedIndexes();
48916                         for (var i = 0; i < selectedIndices.length; i++) {
48917                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48918                         }
48919                         if (selNodes.length == 1) {
48920                                 dragData.ddel = target.cloneNode(true); // the div element
48921                         } else {
48922                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48923                                 div.className = 'multi-proxy';
48924                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48925                                         div.appendChild(selNodes[i].cloneNode(true));
48926                                 }
48927                                 dragData.ddel = div;
48928                         }
48929             //console.log(dragData)
48930             //console.log(dragData.ddel.innerHTML)
48931                         return dragData;
48932                 }
48933         //console.log('nodragData')
48934                 return false;
48935     },
48936     
48937 /**     Specify to which ddGroup items in this DDView may be dragged. */
48938     setDraggable: function(ddGroup) {
48939         if (ddGroup instanceof Array) {
48940                 Roo.each(ddGroup, this.setDraggable, this);
48941                 return;
48942         }
48943         if (this.dragZone) {
48944                 this.dragZone.addToGroup(ddGroup);
48945         } else {
48946                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48947                                 containerScroll: true,
48948                                 ddGroup: ddGroup 
48949
48950                         });
48951 //                      Draggability implies selection. DragZone's mousedown selects the element.
48952                         if (!this.multiSelect) { this.singleSelect = true; }
48953
48954 //                      Wire the DragZone's handlers up to methods in *this*
48955                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48956                 }
48957     },
48958
48959 /**     Specify from which ddGroup this DDView accepts drops. */
48960     setDroppable: function(ddGroup) {
48961         if (ddGroup instanceof Array) {
48962                 Roo.each(ddGroup, this.setDroppable, this);
48963                 return;
48964         }
48965         if (this.dropZone) {
48966                 this.dropZone.addToGroup(ddGroup);
48967         } else {
48968                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48969                                 containerScroll: true,
48970                                 ddGroup: ddGroup
48971                         });
48972
48973 //                      Wire the DropZone's handlers up to methods in *this*
48974                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48975                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48976                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48977                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48978                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48979                 }
48980     },
48981
48982 /**     Decide whether to drop above or below a View node. */
48983     getDropPoint : function(e, n, dd){
48984         if (n == this.el.dom) { return "above"; }
48985                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48986                 var c = t + (b - t) / 2;
48987                 var y = Roo.lib.Event.getPageY(e);
48988                 if(y <= c) {
48989                         return "above";
48990                 }else{
48991                         return "below";
48992                 }
48993     },
48994
48995     onNodeEnter : function(n, dd, e, data){
48996                 return false;
48997     },
48998     
48999     onNodeOver : function(n, dd, e, data){
49000                 var pt = this.getDropPoint(e, n, dd);
49001                 // set the insert point style on the target node
49002                 var dragElClass = this.dropNotAllowed;
49003                 if (pt) {
49004                         var targetElClass;
49005                         if (pt == "above"){
49006                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
49007                                 targetElClass = "x-view-drag-insert-above";
49008                         } else {
49009                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
49010                                 targetElClass = "x-view-drag-insert-below";
49011                         }
49012                         if (this.lastInsertClass != targetElClass){
49013                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
49014                                 this.lastInsertClass = targetElClass;
49015                         }
49016                 }
49017                 return dragElClass;
49018         },
49019
49020     onNodeOut : function(n, dd, e, data){
49021                 this.removeDropIndicators(n);
49022     },
49023
49024     onNodeDrop : function(n, dd, e, data){
49025         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
49026                 return false;
49027         }
49028         var pt = this.getDropPoint(e, n, dd);
49029                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
49030                 if (pt == "below") { insertAt++; }
49031                 for (var i = 0; i < data.records.length; i++) {
49032                         var r = data.records[i];
49033                         var dup = this.store.getById(r.id);
49034                         if (dup && (dd != this.dragZone)) {
49035                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
49036                         } else {
49037                                 if (data.copy) {
49038                                         this.store.insert(insertAt++, r.copy());
49039                                 } else {
49040                                         data.source.isDirtyFlag = true;
49041                                         r.store.remove(r);
49042                                         this.store.insert(insertAt++, r);
49043                                 }
49044                                 this.isDirtyFlag = true;
49045                         }
49046                 }
49047                 this.dragZone.cachedTarget = null;
49048                 return true;
49049     },
49050
49051     removeDropIndicators : function(n){
49052                 if(n){
49053                         Roo.fly(n).removeClass([
49054                                 "x-view-drag-insert-above",
49055                                 "x-view-drag-insert-below"]);
49056                         this.lastInsertClass = "_noclass";
49057                 }
49058     },
49059
49060 /**
49061  *      Utility method. Add a delete option to the DDView's context menu.
49062  *      @param {String} imageUrl The URL of the "delete" icon image.
49063  */
49064         setDeletable: function(imageUrl) {
49065                 if (!this.singleSelect && !this.multiSelect) {
49066                         this.singleSelect = true;
49067                 }
49068                 var c = this.getContextMenu();
49069                 this.contextMenu.on("itemclick", function(item) {
49070                         switch (item.id) {
49071                                 case "delete":
49072                                         this.remove(this.getSelectedIndexes());
49073                                         break;
49074                         }
49075                 }, this);
49076                 this.contextMenu.add({
49077                         icon: imageUrl,
49078                         id: "delete",
49079                         text: 'Delete'
49080                 });
49081         },
49082         
49083 /**     Return the context menu for this DDView. */
49084         getContextMenu: function() {
49085                 if (!this.contextMenu) {
49086 //                      Create the View's context menu
49087                         this.contextMenu = new Roo.menu.Menu({
49088                                 id: this.id + "-contextmenu"
49089                         });
49090                         this.el.on("contextmenu", this.showContextMenu, this);
49091                 }
49092                 return this.contextMenu;
49093         },
49094         
49095         disableContextMenu: function() {
49096                 if (this.contextMenu) {
49097                         this.el.un("contextmenu", this.showContextMenu, this);
49098                 }
49099         },
49100
49101         showContextMenu: function(e, item) {
49102         item = this.findItemFromChild(e.getTarget());
49103                 if (item) {
49104                         e.stopEvent();
49105                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
49106                         this.contextMenu.showAt(e.getXY());
49107             }
49108     },
49109
49110 /**
49111  *      Remove {@link Roo.data.Record}s at the specified indices.
49112  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49113  */
49114     remove: function(selectedIndices) {
49115                 selectedIndices = [].concat(selectedIndices);
49116                 for (var i = 0; i < selectedIndices.length; i++) {
49117                         var rec = this.store.getAt(selectedIndices[i]);
49118                         this.store.remove(rec);
49119                 }
49120     },
49121
49122 /**
49123  *      Double click fires the event, but also, if this is draggable, and there is only one other
49124  *      related DropZone, it transfers the selected node.
49125  */
49126     onDblClick : function(e){
49127         var item = this.findItemFromChild(e.getTarget());
49128         if(item){
49129             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49130                 return false;
49131             }
49132             if (this.dragGroup) {
49133                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49134                     while (targets.indexOf(this.dropZone) > -1) {
49135                             targets.remove(this.dropZone);
49136                                 }
49137                     if (targets.length == 1) {
49138                                         this.dragZone.cachedTarget = null;
49139                         var el = Roo.get(targets[0].getEl());
49140                         var box = el.getBox(true);
49141                         targets[0].onNodeDrop(el.dom, {
49142                                 target: el.dom,
49143                                 xy: [box.x, box.y + box.height - 1]
49144                         }, null, this.getDragData(e));
49145                     }
49146                 }
49147         }
49148     },
49149     
49150     handleSelection: function(e) {
49151                 this.dragZone.cachedTarget = null;
49152         var item = this.findItemFromChild(e.getTarget());
49153         if (!item) {
49154                 this.clearSelections(true);
49155                 return;
49156         }
49157                 if (item && (this.multiSelect || this.singleSelect)){
49158                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49159                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49160                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49161                                 this.unselect(item);
49162                         } else {
49163                                 this.select(item, this.multiSelect && e.ctrlKey);
49164                                 this.lastSelection = item;
49165                         }
49166                 }
49167     },
49168
49169     onItemClick : function(item, index, e){
49170                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49171                         return false;
49172                 }
49173                 return true;
49174     },
49175
49176     unselect : function(nodeInfo, suppressEvent){
49177                 var node = this.getNode(nodeInfo);
49178                 if(node && this.isSelected(node)){
49179                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49180                                 Roo.fly(node).removeClass(this.selectedClass);
49181                                 this.selections.remove(node);
49182                                 if(!suppressEvent){
49183                                         this.fireEvent("selectionchange", this, this.selections);
49184                                 }
49185                         }
49186                 }
49187     }
49188 });
49189 /*
49190  * Based on:
49191  * Ext JS Library 1.1.1
49192  * Copyright(c) 2006-2007, Ext JS, LLC.
49193  *
49194  * Originally Released Under LGPL - original licence link has changed is not relivant.
49195  *
49196  * Fork - LGPL
49197  * <script type="text/javascript">
49198  */
49199  
49200 /**
49201  * @class Roo.LayoutManager
49202  * @extends Roo.util.Observable
49203  * Base class for layout managers.
49204  */
49205 Roo.LayoutManager = function(container, config){
49206     Roo.LayoutManager.superclass.constructor.call(this);
49207     this.el = Roo.get(container);
49208     // ie scrollbar fix
49209     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49210         document.body.scroll = "no";
49211     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49212         this.el.position('relative');
49213     }
49214     this.id = this.el.id;
49215     this.el.addClass("x-layout-container");
49216     /** false to disable window resize monitoring @type Boolean */
49217     this.monitorWindowResize = true;
49218     this.regions = {};
49219     this.addEvents({
49220         /**
49221          * @event layout
49222          * Fires when a layout is performed. 
49223          * @param {Roo.LayoutManager} this
49224          */
49225         "layout" : true,
49226         /**
49227          * @event regionresized
49228          * Fires when the user resizes a region. 
49229          * @param {Roo.LayoutRegion} region The resized region
49230          * @param {Number} newSize The new size (width for east/west, height for north/south)
49231          */
49232         "regionresized" : true,
49233         /**
49234          * @event regioncollapsed
49235          * Fires when a region is collapsed. 
49236          * @param {Roo.LayoutRegion} region The collapsed region
49237          */
49238         "regioncollapsed" : true,
49239         /**
49240          * @event regionexpanded
49241          * Fires when a region is expanded.  
49242          * @param {Roo.LayoutRegion} region The expanded region
49243          */
49244         "regionexpanded" : true
49245     });
49246     this.updating = false;
49247     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49248 };
49249
49250 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49251     /**
49252      * Returns true if this layout is currently being updated
49253      * @return {Boolean}
49254      */
49255     isUpdating : function(){
49256         return this.updating; 
49257     },
49258     
49259     /**
49260      * Suspend the LayoutManager from doing auto-layouts while
49261      * making multiple add or remove calls
49262      */
49263     beginUpdate : function(){
49264         this.updating = true;    
49265     },
49266     
49267     /**
49268      * Restore auto-layouts and optionally disable the manager from performing a layout
49269      * @param {Boolean} noLayout true to disable a layout update 
49270      */
49271     endUpdate : function(noLayout){
49272         this.updating = false;
49273         if(!noLayout){
49274             this.layout();
49275         }    
49276     },
49277     
49278     layout: function(){
49279         
49280     },
49281     
49282     onRegionResized : function(region, newSize){
49283         this.fireEvent("regionresized", region, newSize);
49284         this.layout();
49285     },
49286     
49287     onRegionCollapsed : function(region){
49288         this.fireEvent("regioncollapsed", region);
49289     },
49290     
49291     onRegionExpanded : function(region){
49292         this.fireEvent("regionexpanded", region);
49293     },
49294         
49295     /**
49296      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49297      * performs box-model adjustments.
49298      * @return {Object} The size as an object {width: (the width), height: (the height)}
49299      */
49300     getViewSize : function(){
49301         var size;
49302         if(this.el.dom != document.body){
49303             size = this.el.getSize();
49304         }else{
49305             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49306         }
49307         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49308         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49309         return size;
49310     },
49311     
49312     /**
49313      * Returns the Element this layout is bound to.
49314      * @return {Roo.Element}
49315      */
49316     getEl : function(){
49317         return this.el;
49318     },
49319     
49320     /**
49321      * Returns the specified region.
49322      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49323      * @return {Roo.LayoutRegion}
49324      */
49325     getRegion : function(target){
49326         return this.regions[target.toLowerCase()];
49327     },
49328     
49329     onWindowResize : function(){
49330         if(this.monitorWindowResize){
49331             this.layout();
49332         }
49333     }
49334 });/*
49335  * Based on:
49336  * Ext JS Library 1.1.1
49337  * Copyright(c) 2006-2007, Ext JS, LLC.
49338  *
49339  * Originally Released Under LGPL - original licence link has changed is not relivant.
49340  *
49341  * Fork - LGPL
49342  * <script type="text/javascript">
49343  */
49344 /**
49345  * @class Roo.BorderLayout
49346  * @extends Roo.LayoutManager
49347  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49348  * please see: <br><br>
49349  * <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>
49350  * <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>
49351  * Example:
49352  <pre><code>
49353  var layout = new Roo.BorderLayout(document.body, {
49354     north: {
49355         initialSize: 25,
49356         titlebar: false
49357     },
49358     west: {
49359         split:true,
49360         initialSize: 200,
49361         minSize: 175,
49362         maxSize: 400,
49363         titlebar: true,
49364         collapsible: true
49365     },
49366     east: {
49367         split:true,
49368         initialSize: 202,
49369         minSize: 175,
49370         maxSize: 400,
49371         titlebar: true,
49372         collapsible: true
49373     },
49374     south: {
49375         split:true,
49376         initialSize: 100,
49377         minSize: 100,
49378         maxSize: 200,
49379         titlebar: true,
49380         collapsible: true
49381     },
49382     center: {
49383         titlebar: true,
49384         autoScroll:true,
49385         resizeTabs: true,
49386         minTabWidth: 50,
49387         preferredTabWidth: 150
49388     }
49389 });
49390
49391 // shorthand
49392 var CP = Roo.ContentPanel;
49393
49394 layout.beginUpdate();
49395 layout.add("north", new CP("north", "North"));
49396 layout.add("south", new CP("south", {title: "South", closable: true}));
49397 layout.add("west", new CP("west", {title: "West"}));
49398 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49399 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49400 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49401 layout.getRegion("center").showPanel("center1");
49402 layout.endUpdate();
49403 </code></pre>
49404
49405 <b>The container the layout is rendered into can be either the body element or any other element.
49406 If it is not the body element, the container needs to either be an absolute positioned element,
49407 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49408 the container size if it is not the body element.</b>
49409
49410 * @constructor
49411 * Create a new BorderLayout
49412 * @param {String/HTMLElement/Element} container The container this layout is bound to
49413 * @param {Object} config Configuration options
49414  */
49415 Roo.BorderLayout = function(container, config){
49416     config = config || {};
49417     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49418     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49419     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49420         var target = this.factory.validRegions[i];
49421         if(config[target]){
49422             this.addRegion(target, config[target]);
49423         }
49424     }
49425 };
49426
49427 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49428     /**
49429      * Creates and adds a new region if it doesn't already exist.
49430      * @param {String} target The target region key (north, south, east, west or center).
49431      * @param {Object} config The regions config object
49432      * @return {BorderLayoutRegion} The new region
49433      */
49434     addRegion : function(target, config){
49435         if(!this.regions[target]){
49436             var r = this.factory.create(target, this, config);
49437             this.bindRegion(target, r);
49438         }
49439         return this.regions[target];
49440     },
49441
49442     // private (kinda)
49443     bindRegion : function(name, r){
49444         this.regions[name] = r;
49445         r.on("visibilitychange", this.layout, this);
49446         r.on("paneladded", this.layout, this);
49447         r.on("panelremoved", this.layout, this);
49448         r.on("invalidated", this.layout, this);
49449         r.on("resized", this.onRegionResized, this);
49450         r.on("collapsed", this.onRegionCollapsed, this);
49451         r.on("expanded", this.onRegionExpanded, this);
49452     },
49453
49454     /**
49455      * Performs a layout update.
49456      */
49457     layout : function(){
49458         if(this.updating) {
49459             return;
49460         }
49461         var size = this.getViewSize();
49462         var w = size.width;
49463         var h = size.height;
49464         var centerW = w;
49465         var centerH = h;
49466         var centerY = 0;
49467         var centerX = 0;
49468         //var x = 0, y = 0;
49469
49470         var rs = this.regions;
49471         var north = rs["north"];
49472         var south = rs["south"]; 
49473         var west = rs["west"];
49474         var east = rs["east"];
49475         var center = rs["center"];
49476         //if(this.hideOnLayout){ // not supported anymore
49477             //c.el.setStyle("display", "none");
49478         //}
49479         if(north && north.isVisible()){
49480             var b = north.getBox();
49481             var m = north.getMargins();
49482             b.width = w - (m.left+m.right);
49483             b.x = m.left;
49484             b.y = m.top;
49485             centerY = b.height + b.y + m.bottom;
49486             centerH -= centerY;
49487             north.updateBox(this.safeBox(b));
49488         }
49489         if(south && south.isVisible()){
49490             var b = south.getBox();
49491             var m = south.getMargins();
49492             b.width = w - (m.left+m.right);
49493             b.x = m.left;
49494             var totalHeight = (b.height + m.top + m.bottom);
49495             b.y = h - totalHeight + m.top;
49496             centerH -= totalHeight;
49497             south.updateBox(this.safeBox(b));
49498         }
49499         if(west && west.isVisible()){
49500             var b = west.getBox();
49501             var m = west.getMargins();
49502             b.height = centerH - (m.top+m.bottom);
49503             b.x = m.left;
49504             b.y = centerY + m.top;
49505             var totalWidth = (b.width + m.left + m.right);
49506             centerX += totalWidth;
49507             centerW -= totalWidth;
49508             west.updateBox(this.safeBox(b));
49509         }
49510         if(east && east.isVisible()){
49511             var b = east.getBox();
49512             var m = east.getMargins();
49513             b.height = centerH - (m.top+m.bottom);
49514             var totalWidth = (b.width + m.left + m.right);
49515             b.x = w - totalWidth + m.left;
49516             b.y = centerY + m.top;
49517             centerW -= totalWidth;
49518             east.updateBox(this.safeBox(b));
49519         }
49520         if(center){
49521             var m = center.getMargins();
49522             var centerBox = {
49523                 x: centerX + m.left,
49524                 y: centerY + m.top,
49525                 width: centerW - (m.left+m.right),
49526                 height: centerH - (m.top+m.bottom)
49527             };
49528             //if(this.hideOnLayout){
49529                 //center.el.setStyle("display", "block");
49530             //}
49531             center.updateBox(this.safeBox(centerBox));
49532         }
49533         this.el.repaint();
49534         this.fireEvent("layout", this);
49535     },
49536
49537     // private
49538     safeBox : function(box){
49539         box.width = Math.max(0, box.width);
49540         box.height = Math.max(0, box.height);
49541         return box;
49542     },
49543
49544     /**
49545      * Adds a ContentPanel (or subclass) to this layout.
49546      * @param {String} target The target region key (north, south, east, west or center).
49547      * @param {Roo.ContentPanel} panel The panel to add
49548      * @return {Roo.ContentPanel} The added panel
49549      */
49550     add : function(target, panel){
49551          
49552         target = target.toLowerCase();
49553         return this.regions[target].add(panel);
49554     },
49555
49556     /**
49557      * Remove a ContentPanel (or subclass) to this layout.
49558      * @param {String} target The target region key (north, south, east, west or center).
49559      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49560      * @return {Roo.ContentPanel} The removed panel
49561      */
49562     remove : function(target, panel){
49563         target = target.toLowerCase();
49564         return this.regions[target].remove(panel);
49565     },
49566
49567     /**
49568      * Searches all regions for a panel with the specified id
49569      * @param {String} panelId
49570      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49571      */
49572     findPanel : function(panelId){
49573         var rs = this.regions;
49574         for(var target in rs){
49575             if(typeof rs[target] != "function"){
49576                 var p = rs[target].getPanel(panelId);
49577                 if(p){
49578                     return p;
49579                 }
49580             }
49581         }
49582         return null;
49583     },
49584
49585     /**
49586      * Searches all regions for a panel with the specified id and activates (shows) it.
49587      * @param {String/ContentPanel} panelId The panels id or the panel itself
49588      * @return {Roo.ContentPanel} The shown panel or null
49589      */
49590     showPanel : function(panelId) {
49591       var rs = this.regions;
49592       for(var target in rs){
49593          var r = rs[target];
49594          if(typeof r != "function"){
49595             if(r.hasPanel(panelId)){
49596                return r.showPanel(panelId);
49597             }
49598          }
49599       }
49600       return null;
49601    },
49602
49603    /**
49604      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49605      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49606      */
49607     restoreState : function(provider){
49608         if(!provider){
49609             provider = Roo.state.Manager;
49610         }
49611         var sm = new Roo.LayoutStateManager();
49612         sm.init(this, provider);
49613     },
49614
49615     /**
49616      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49617      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49618      * a valid ContentPanel config object.  Example:
49619      * <pre><code>
49620 // Create the main layout
49621 var layout = new Roo.BorderLayout('main-ct', {
49622     west: {
49623         split:true,
49624         minSize: 175,
49625         titlebar: true
49626     },
49627     center: {
49628         title:'Components'
49629     }
49630 }, 'main-ct');
49631
49632 // Create and add multiple ContentPanels at once via configs
49633 layout.batchAdd({
49634    west: {
49635        id: 'source-files',
49636        autoCreate:true,
49637        title:'Ext Source Files',
49638        autoScroll:true,
49639        fitToFrame:true
49640    },
49641    center : {
49642        el: cview,
49643        autoScroll:true,
49644        fitToFrame:true,
49645        toolbar: tb,
49646        resizeEl:'cbody'
49647    }
49648 });
49649 </code></pre>
49650      * @param {Object} regions An object containing ContentPanel configs by region name
49651      */
49652     batchAdd : function(regions){
49653         this.beginUpdate();
49654         for(var rname in regions){
49655             var lr = this.regions[rname];
49656             if(lr){
49657                 this.addTypedPanels(lr, regions[rname]);
49658             }
49659         }
49660         this.endUpdate();
49661     },
49662
49663     // private
49664     addTypedPanels : function(lr, ps){
49665         if(typeof ps == 'string'){
49666             lr.add(new Roo.ContentPanel(ps));
49667         }
49668         else if(ps instanceof Array){
49669             for(var i =0, len = ps.length; i < len; i++){
49670                 this.addTypedPanels(lr, ps[i]);
49671             }
49672         }
49673         else if(!ps.events){ // raw config?
49674             var el = ps.el;
49675             delete ps.el; // prevent conflict
49676             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49677         }
49678         else {  // panel object assumed!
49679             lr.add(ps);
49680         }
49681     },
49682     /**
49683      * Adds a xtype elements to the layout.
49684      * <pre><code>
49685
49686 layout.addxtype({
49687        xtype : 'ContentPanel',
49688        region: 'west',
49689        items: [ .... ]
49690    }
49691 );
49692
49693 layout.addxtype({
49694         xtype : 'NestedLayoutPanel',
49695         region: 'west',
49696         layout: {
49697            center: { },
49698            west: { }   
49699         },
49700         items : [ ... list of content panels or nested layout panels.. ]
49701    }
49702 );
49703 </code></pre>
49704      * @param {Object} cfg Xtype definition of item to add.
49705      */
49706     addxtype : function(cfg)
49707     {
49708         // basically accepts a pannel...
49709         // can accept a layout region..!?!?
49710         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49711         
49712         if (!cfg.xtype.match(/Panel$/)) {
49713             return false;
49714         }
49715         var ret = false;
49716         
49717         if (typeof(cfg.region) == 'undefined') {
49718             Roo.log("Failed to add Panel, region was not set");
49719             Roo.log(cfg);
49720             return false;
49721         }
49722         var region = cfg.region;
49723         delete cfg.region;
49724         
49725           
49726         var xitems = [];
49727         if (cfg.items) {
49728             xitems = cfg.items;
49729             delete cfg.items;
49730         }
49731         var nb = false;
49732         
49733         switch(cfg.xtype) 
49734         {
49735             case 'ContentPanel':  // ContentPanel (el, cfg)
49736             case 'ScrollPanel':  // ContentPanel (el, cfg)
49737             case 'ViewPanel': 
49738                 if(cfg.autoCreate) {
49739                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49740                 } else {
49741                     var el = this.el.createChild();
49742                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49743                 }
49744                 
49745                 this.add(region, ret);
49746                 break;
49747             
49748             
49749             case 'TreePanel': // our new panel!
49750                 cfg.el = this.el.createChild();
49751                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49752                 this.add(region, ret);
49753                 break;
49754             
49755             case 'NestedLayoutPanel': 
49756                 // create a new Layout (which is  a Border Layout...
49757                 var el = this.el.createChild();
49758                 var clayout = cfg.layout;
49759                 delete cfg.layout;
49760                 clayout.items   = clayout.items  || [];
49761                 // replace this exitems with the clayout ones..
49762                 xitems = clayout.items;
49763                  
49764                 
49765                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49766                     cfg.background = false;
49767                 }
49768                 var layout = new Roo.BorderLayout(el, clayout);
49769                 
49770                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49771                 //console.log('adding nested layout panel '  + cfg.toSource());
49772                 this.add(region, ret);
49773                 nb = {}; /// find first...
49774                 break;
49775                 
49776             case 'GridPanel': 
49777             
49778                 // needs grid and region
49779                 
49780                 //var el = this.getRegion(region).el.createChild();
49781                 var el = this.el.createChild();
49782                 // create the grid first...
49783                 
49784                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49785                 delete cfg.grid;
49786                 if (region == 'center' && this.active ) {
49787                     cfg.background = false;
49788                 }
49789                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49790                 
49791                 this.add(region, ret);
49792                 if (cfg.background) {
49793                     ret.on('activate', function(gp) {
49794                         if (!gp.grid.rendered) {
49795                             gp.grid.render();
49796                         }
49797                     });
49798                 } else {
49799                     grid.render();
49800                 }
49801                 break;
49802            
49803            
49804            
49805                 
49806                 
49807                 
49808             default:
49809                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49810                     
49811                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49812                     this.add(region, ret);
49813                 } else {
49814                 
49815                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49816                     return null;
49817                 }
49818                 
49819              // GridPanel (grid, cfg)
49820             
49821         }
49822         this.beginUpdate();
49823         // add children..
49824         var region = '';
49825         var abn = {};
49826         Roo.each(xitems, function(i)  {
49827             region = nb && i.region ? i.region : false;
49828             
49829             var add = ret.addxtype(i);
49830            
49831             if (region) {
49832                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49833                 if (!i.background) {
49834                     abn[region] = nb[region] ;
49835                 }
49836             }
49837             
49838         });
49839         this.endUpdate();
49840
49841         // make the last non-background panel active..
49842         //if (nb) { Roo.log(abn); }
49843         if (nb) {
49844             
49845             for(var r in abn) {
49846                 region = this.getRegion(r);
49847                 if (region) {
49848                     // tried using nb[r], but it does not work..
49849                      
49850                     region.showPanel(abn[r]);
49851                    
49852                 }
49853             }
49854         }
49855         return ret;
49856         
49857     }
49858 });
49859
49860 /**
49861  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49862  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49863  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49864  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49865  * <pre><code>
49866 // shorthand
49867 var CP = Roo.ContentPanel;
49868
49869 var layout = Roo.BorderLayout.create({
49870     north: {
49871         initialSize: 25,
49872         titlebar: false,
49873         panels: [new CP("north", "North")]
49874     },
49875     west: {
49876         split:true,
49877         initialSize: 200,
49878         minSize: 175,
49879         maxSize: 400,
49880         titlebar: true,
49881         collapsible: true,
49882         panels: [new CP("west", {title: "West"})]
49883     },
49884     east: {
49885         split:true,
49886         initialSize: 202,
49887         minSize: 175,
49888         maxSize: 400,
49889         titlebar: true,
49890         collapsible: true,
49891         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49892     },
49893     south: {
49894         split:true,
49895         initialSize: 100,
49896         minSize: 100,
49897         maxSize: 200,
49898         titlebar: true,
49899         collapsible: true,
49900         panels: [new CP("south", {title: "South", closable: true})]
49901     },
49902     center: {
49903         titlebar: true,
49904         autoScroll:true,
49905         resizeTabs: true,
49906         minTabWidth: 50,
49907         preferredTabWidth: 150,
49908         panels: [
49909             new CP("center1", {title: "Close Me", closable: true}),
49910             new CP("center2", {title: "Center Panel", closable: false})
49911         ]
49912     }
49913 }, document.body);
49914
49915 layout.getRegion("center").showPanel("center1");
49916 </code></pre>
49917  * @param config
49918  * @param targetEl
49919  */
49920 Roo.BorderLayout.create = function(config, targetEl){
49921     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49922     layout.beginUpdate();
49923     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49924     for(var j = 0, jlen = regions.length; j < jlen; j++){
49925         var lr = regions[j];
49926         if(layout.regions[lr] && config[lr].panels){
49927             var r = layout.regions[lr];
49928             var ps = config[lr].panels;
49929             layout.addTypedPanels(r, ps);
49930         }
49931     }
49932     layout.endUpdate();
49933     return layout;
49934 };
49935
49936 // private
49937 Roo.BorderLayout.RegionFactory = {
49938     // private
49939     validRegions : ["north","south","east","west","center"],
49940
49941     // private
49942     create : function(target, mgr, config){
49943         target = target.toLowerCase();
49944         if(config.lightweight || config.basic){
49945             return new Roo.BasicLayoutRegion(mgr, config, target);
49946         }
49947         switch(target){
49948             case "north":
49949                 return new Roo.NorthLayoutRegion(mgr, config);
49950             case "south":
49951                 return new Roo.SouthLayoutRegion(mgr, config);
49952             case "east":
49953                 return new Roo.EastLayoutRegion(mgr, config);
49954             case "west":
49955                 return new Roo.WestLayoutRegion(mgr, config);
49956             case "center":
49957                 return new Roo.CenterLayoutRegion(mgr, config);
49958         }
49959         throw 'Layout region "'+target+'" not supported.';
49960     }
49961 };/*
49962  * Based on:
49963  * Ext JS Library 1.1.1
49964  * Copyright(c) 2006-2007, Ext JS, LLC.
49965  *
49966  * Originally Released Under LGPL - original licence link has changed is not relivant.
49967  *
49968  * Fork - LGPL
49969  * <script type="text/javascript">
49970  */
49971  
49972 /**
49973  * @class Roo.BasicLayoutRegion
49974  * @extends Roo.util.Observable
49975  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49976  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49977  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49978  */
49979 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49980     this.mgr = mgr;
49981     this.position  = pos;
49982     this.events = {
49983         /**
49984          * @scope Roo.BasicLayoutRegion
49985          */
49986         
49987         /**
49988          * @event beforeremove
49989          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49990          * @param {Roo.LayoutRegion} this
49991          * @param {Roo.ContentPanel} panel The panel
49992          * @param {Object} e The cancel event object
49993          */
49994         "beforeremove" : true,
49995         /**
49996          * @event invalidated
49997          * Fires when the layout for this region is changed.
49998          * @param {Roo.LayoutRegion} this
49999          */
50000         "invalidated" : true,
50001         /**
50002          * @event visibilitychange
50003          * Fires when this region is shown or hidden 
50004          * @param {Roo.LayoutRegion} this
50005          * @param {Boolean} visibility true or false
50006          */
50007         "visibilitychange" : true,
50008         /**
50009          * @event paneladded
50010          * Fires when a panel is added. 
50011          * @param {Roo.LayoutRegion} this
50012          * @param {Roo.ContentPanel} panel The panel
50013          */
50014         "paneladded" : true,
50015         /**
50016          * @event panelremoved
50017          * Fires when a panel is removed. 
50018          * @param {Roo.LayoutRegion} this
50019          * @param {Roo.ContentPanel} panel The panel
50020          */
50021         "panelremoved" : true,
50022         /**
50023          * @event collapsed
50024          * Fires when this region is collapsed.
50025          * @param {Roo.LayoutRegion} this
50026          */
50027         "collapsed" : true,
50028         /**
50029          * @event expanded
50030          * Fires when this region is expanded.
50031          * @param {Roo.LayoutRegion} this
50032          */
50033         "expanded" : true,
50034         /**
50035          * @event slideshow
50036          * Fires when this region is slid into view.
50037          * @param {Roo.LayoutRegion} this
50038          */
50039         "slideshow" : true,
50040         /**
50041          * @event slidehide
50042          * Fires when this region slides out of view. 
50043          * @param {Roo.LayoutRegion} this
50044          */
50045         "slidehide" : true,
50046         /**
50047          * @event panelactivated
50048          * Fires when a panel is activated. 
50049          * @param {Roo.LayoutRegion} this
50050          * @param {Roo.ContentPanel} panel The activated panel
50051          */
50052         "panelactivated" : true,
50053         /**
50054          * @event resized
50055          * Fires when the user resizes this region. 
50056          * @param {Roo.LayoutRegion} this
50057          * @param {Number} newSize The new size (width for east/west, height for north/south)
50058          */
50059         "resized" : true
50060     };
50061     /** A collection of panels in this region. @type Roo.util.MixedCollection */
50062     this.panels = new Roo.util.MixedCollection();
50063     this.panels.getKey = this.getPanelId.createDelegate(this);
50064     this.box = null;
50065     this.activePanel = null;
50066     // ensure listeners are added...
50067     
50068     if (config.listeners || config.events) {
50069         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
50070             listeners : config.listeners || {},
50071             events : config.events || {}
50072         });
50073     }
50074     
50075     if(skipConfig !== true){
50076         this.applyConfig(config);
50077     }
50078 };
50079
50080 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
50081     getPanelId : function(p){
50082         return p.getId();
50083     },
50084     
50085     applyConfig : function(config){
50086         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50087         this.config = config;
50088         
50089     },
50090     
50091     /**
50092      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
50093      * the width, for horizontal (north, south) the height.
50094      * @param {Number} newSize The new width or height
50095      */
50096     resizeTo : function(newSize){
50097         var el = this.el ? this.el :
50098                  (this.activePanel ? this.activePanel.getEl() : null);
50099         if(el){
50100             switch(this.position){
50101                 case "east":
50102                 case "west":
50103                     el.setWidth(newSize);
50104                     this.fireEvent("resized", this, newSize);
50105                 break;
50106                 case "north":
50107                 case "south":
50108                     el.setHeight(newSize);
50109                     this.fireEvent("resized", this, newSize);
50110                 break;                
50111             }
50112         }
50113     },
50114     
50115     getBox : function(){
50116         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50117     },
50118     
50119     getMargins : function(){
50120         return this.margins;
50121     },
50122     
50123     updateBox : function(box){
50124         this.box = box;
50125         var el = this.activePanel.getEl();
50126         el.dom.style.left = box.x + "px";
50127         el.dom.style.top = box.y + "px";
50128         this.activePanel.setSize(box.width, box.height);
50129     },
50130     
50131     /**
50132      * Returns the container element for this region.
50133      * @return {Roo.Element}
50134      */
50135     getEl : function(){
50136         return this.activePanel;
50137     },
50138     
50139     /**
50140      * Returns true if this region is currently visible.
50141      * @return {Boolean}
50142      */
50143     isVisible : function(){
50144         return this.activePanel ? true : false;
50145     },
50146     
50147     setActivePanel : function(panel){
50148         panel = this.getPanel(panel);
50149         if(this.activePanel && this.activePanel != panel){
50150             this.activePanel.setActiveState(false);
50151             this.activePanel.getEl().setLeftTop(-10000,-10000);
50152         }
50153         this.activePanel = panel;
50154         panel.setActiveState(true);
50155         if(this.box){
50156             panel.setSize(this.box.width, this.box.height);
50157         }
50158         this.fireEvent("panelactivated", this, panel);
50159         this.fireEvent("invalidated");
50160     },
50161     
50162     /**
50163      * Show the specified panel.
50164      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50165      * @return {Roo.ContentPanel} The shown panel or null
50166      */
50167     showPanel : function(panel){
50168         if(panel = this.getPanel(panel)){
50169             this.setActivePanel(panel);
50170         }
50171         return panel;
50172     },
50173     
50174     /**
50175      * Get the active panel for this region.
50176      * @return {Roo.ContentPanel} The active panel or null
50177      */
50178     getActivePanel : function(){
50179         return this.activePanel;
50180     },
50181     
50182     /**
50183      * Add the passed ContentPanel(s)
50184      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50185      * @return {Roo.ContentPanel} The panel added (if only one was added)
50186      */
50187     add : function(panel){
50188         if(arguments.length > 1){
50189             for(var i = 0, len = arguments.length; i < len; i++) {
50190                 this.add(arguments[i]);
50191             }
50192             return null;
50193         }
50194         if(this.hasPanel(panel)){
50195             this.showPanel(panel);
50196             return panel;
50197         }
50198         var el = panel.getEl();
50199         if(el.dom.parentNode != this.mgr.el.dom){
50200             this.mgr.el.dom.appendChild(el.dom);
50201         }
50202         if(panel.setRegion){
50203             panel.setRegion(this);
50204         }
50205         this.panels.add(panel);
50206         el.setStyle("position", "absolute");
50207         if(!panel.background){
50208             this.setActivePanel(panel);
50209             if(this.config.initialSize && this.panels.getCount()==1){
50210                 this.resizeTo(this.config.initialSize);
50211             }
50212         }
50213         this.fireEvent("paneladded", this, panel);
50214         return panel;
50215     },
50216     
50217     /**
50218      * Returns true if the panel is in this region.
50219      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50220      * @return {Boolean}
50221      */
50222     hasPanel : function(panel){
50223         if(typeof panel == "object"){ // must be panel obj
50224             panel = panel.getId();
50225         }
50226         return this.getPanel(panel) ? true : false;
50227     },
50228     
50229     /**
50230      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50231      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50232      * @param {Boolean} preservePanel Overrides the config preservePanel option
50233      * @return {Roo.ContentPanel} The panel that was removed
50234      */
50235     remove : function(panel, preservePanel){
50236         panel = this.getPanel(panel);
50237         if(!panel){
50238             return null;
50239         }
50240         var e = {};
50241         this.fireEvent("beforeremove", this, panel, e);
50242         if(e.cancel === true){
50243             return null;
50244         }
50245         var panelId = panel.getId();
50246         this.panels.removeKey(panelId);
50247         return panel;
50248     },
50249     
50250     /**
50251      * Returns the panel specified or null if it's not in this region.
50252      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50253      * @return {Roo.ContentPanel}
50254      */
50255     getPanel : function(id){
50256         if(typeof id == "object"){ // must be panel obj
50257             return id;
50258         }
50259         return this.panels.get(id);
50260     },
50261     
50262     /**
50263      * Returns this regions position (north/south/east/west/center).
50264      * @return {String} 
50265      */
50266     getPosition: function(){
50267         return this.position;    
50268     }
50269 });/*
50270  * Based on:
50271  * Ext JS Library 1.1.1
50272  * Copyright(c) 2006-2007, Ext JS, LLC.
50273  *
50274  * Originally Released Under LGPL - original licence link has changed is not relivant.
50275  *
50276  * Fork - LGPL
50277  * <script type="text/javascript">
50278  */
50279  
50280 /**
50281  * @class Roo.LayoutRegion
50282  * @extends Roo.BasicLayoutRegion
50283  * This class represents a region in a layout manager.
50284  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50285  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50286  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50287  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50288  * @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})
50289  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50290  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50291  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50292  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50293  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50294  * @cfg {String}    title           The title for the region (overrides panel titles)
50295  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50296  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50297  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50298  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50299  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50300  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50301  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50302  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50303  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50304  * @cfg {Boolean}   showPin         True to show a pin button
50305  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50306  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50307  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50308  * @cfg {Number}    width           For East/West panels
50309  * @cfg {Number}    height          For North/South panels
50310  * @cfg {Boolean}   split           To show the splitter
50311  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50312  */
50313 Roo.LayoutRegion = function(mgr, config, pos){
50314     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50315     var dh = Roo.DomHelper;
50316     /** This region's container element 
50317     * @type Roo.Element */
50318     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50319     /** This region's title element 
50320     * @type Roo.Element */
50321
50322     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50323         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50324         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50325     ]}, true);
50326     this.titleEl.enableDisplayMode();
50327     /** This region's title text element 
50328     * @type HTMLElement */
50329     this.titleTextEl = this.titleEl.dom.firstChild;
50330     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50331     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50332     this.closeBtn.enableDisplayMode();
50333     this.closeBtn.on("click", this.closeClicked, this);
50334     this.closeBtn.hide();
50335
50336     this.createBody(config);
50337     this.visible = true;
50338     this.collapsed = false;
50339
50340     if(config.hideWhenEmpty){
50341         this.hide();
50342         this.on("paneladded", this.validateVisibility, this);
50343         this.on("panelremoved", this.validateVisibility, this);
50344     }
50345     this.applyConfig(config);
50346 };
50347
50348 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50349
50350     createBody : function(){
50351         /** This region's body element 
50352         * @type Roo.Element */
50353         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50354     },
50355
50356     applyConfig : function(c){
50357         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50358             var dh = Roo.DomHelper;
50359             if(c.titlebar !== false){
50360                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50361                 this.collapseBtn.on("click", this.collapse, this);
50362                 this.collapseBtn.enableDisplayMode();
50363
50364                 if(c.showPin === true || this.showPin){
50365                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50366                     this.stickBtn.enableDisplayMode();
50367                     this.stickBtn.on("click", this.expand, this);
50368                     this.stickBtn.hide();
50369                 }
50370             }
50371             /** This region's collapsed element
50372             * @type Roo.Element */
50373             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50374                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50375             ]}, true);
50376             if(c.floatable !== false){
50377                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50378                this.collapsedEl.on("click", this.collapseClick, this);
50379             }
50380
50381             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50382                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50383                    id: "message", unselectable: "on", style:{"float":"left"}});
50384                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50385              }
50386             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50387             this.expandBtn.on("click", this.expand, this);
50388         }
50389         if(this.collapseBtn){
50390             this.collapseBtn.setVisible(c.collapsible == true);
50391         }
50392         this.cmargins = c.cmargins || this.cmargins ||
50393                          (this.position == "west" || this.position == "east" ?
50394                              {top: 0, left: 2, right:2, bottom: 0} :
50395                              {top: 2, left: 0, right:0, bottom: 2});
50396         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50397         this.bottomTabs = c.tabPosition != "top";
50398         this.autoScroll = c.autoScroll || false;
50399         if(this.autoScroll){
50400             this.bodyEl.setStyle("overflow", "auto");
50401         }else{
50402             this.bodyEl.setStyle("overflow", "hidden");
50403         }
50404         //if(c.titlebar !== false){
50405             if((!c.titlebar && !c.title) || c.titlebar === false){
50406                 this.titleEl.hide();
50407             }else{
50408                 this.titleEl.show();
50409                 if(c.title){
50410                     this.titleTextEl.innerHTML = c.title;
50411                 }
50412             }
50413         //}
50414         this.duration = c.duration || .30;
50415         this.slideDuration = c.slideDuration || .45;
50416         this.config = c;
50417         if(c.collapsed){
50418             this.collapse(true);
50419         }
50420         if(c.hidden){
50421             this.hide();
50422         }
50423     },
50424     /**
50425      * Returns true if this region is currently visible.
50426      * @return {Boolean}
50427      */
50428     isVisible : function(){
50429         return this.visible;
50430     },
50431
50432     /**
50433      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50434      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50435      */
50436     setCollapsedTitle : function(title){
50437         title = title || "&#160;";
50438         if(this.collapsedTitleTextEl){
50439             this.collapsedTitleTextEl.innerHTML = title;
50440         }
50441     },
50442
50443     getBox : function(){
50444         var b;
50445         if(!this.collapsed){
50446             b = this.el.getBox(false, true);
50447         }else{
50448             b = this.collapsedEl.getBox(false, true);
50449         }
50450         return b;
50451     },
50452
50453     getMargins : function(){
50454         return this.collapsed ? this.cmargins : this.margins;
50455     },
50456
50457     highlight : function(){
50458         this.el.addClass("x-layout-panel-dragover");
50459     },
50460
50461     unhighlight : function(){
50462         this.el.removeClass("x-layout-panel-dragover");
50463     },
50464
50465     updateBox : function(box){
50466         this.box = box;
50467         if(!this.collapsed){
50468             this.el.dom.style.left = box.x + "px";
50469             this.el.dom.style.top = box.y + "px";
50470             this.updateBody(box.width, box.height);
50471         }else{
50472             this.collapsedEl.dom.style.left = box.x + "px";
50473             this.collapsedEl.dom.style.top = box.y + "px";
50474             this.collapsedEl.setSize(box.width, box.height);
50475         }
50476         if(this.tabs){
50477             this.tabs.autoSizeTabs();
50478         }
50479     },
50480
50481     updateBody : function(w, h){
50482         if(w !== null){
50483             this.el.setWidth(w);
50484             w -= this.el.getBorderWidth("rl");
50485             if(this.config.adjustments){
50486                 w += this.config.adjustments[0];
50487             }
50488         }
50489         if(h !== null){
50490             this.el.setHeight(h);
50491             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50492             h -= this.el.getBorderWidth("tb");
50493             if(this.config.adjustments){
50494                 h += this.config.adjustments[1];
50495             }
50496             this.bodyEl.setHeight(h);
50497             if(this.tabs){
50498                 h = this.tabs.syncHeight(h);
50499             }
50500         }
50501         if(this.panelSize){
50502             w = w !== null ? w : this.panelSize.width;
50503             h = h !== null ? h : this.panelSize.height;
50504         }
50505         if(this.activePanel){
50506             var el = this.activePanel.getEl();
50507             w = w !== null ? w : el.getWidth();
50508             h = h !== null ? h : el.getHeight();
50509             this.panelSize = {width: w, height: h};
50510             this.activePanel.setSize(w, h);
50511         }
50512         if(Roo.isIE && this.tabs){
50513             this.tabs.el.repaint();
50514         }
50515     },
50516
50517     /**
50518      * Returns the container element for this region.
50519      * @return {Roo.Element}
50520      */
50521     getEl : function(){
50522         return this.el;
50523     },
50524
50525     /**
50526      * Hides this region.
50527      */
50528     hide : function(){
50529         if(!this.collapsed){
50530             this.el.dom.style.left = "-2000px";
50531             this.el.hide();
50532         }else{
50533             this.collapsedEl.dom.style.left = "-2000px";
50534             this.collapsedEl.hide();
50535         }
50536         this.visible = false;
50537         this.fireEvent("visibilitychange", this, false);
50538     },
50539
50540     /**
50541      * Shows this region if it was previously hidden.
50542      */
50543     show : function(){
50544         if(!this.collapsed){
50545             this.el.show();
50546         }else{
50547             this.collapsedEl.show();
50548         }
50549         this.visible = true;
50550         this.fireEvent("visibilitychange", this, true);
50551     },
50552
50553     closeClicked : function(){
50554         if(this.activePanel){
50555             this.remove(this.activePanel);
50556         }
50557     },
50558
50559     collapseClick : function(e){
50560         if(this.isSlid){
50561            e.stopPropagation();
50562            this.slideIn();
50563         }else{
50564            e.stopPropagation();
50565            this.slideOut();
50566         }
50567     },
50568
50569     /**
50570      * Collapses this region.
50571      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50572      */
50573     collapse : function(skipAnim){
50574         if(this.collapsed) {
50575             return;
50576         }
50577         this.collapsed = true;
50578         if(this.split){
50579             this.split.el.hide();
50580         }
50581         if(this.config.animate && skipAnim !== true){
50582             this.fireEvent("invalidated", this);
50583             this.animateCollapse();
50584         }else{
50585             this.el.setLocation(-20000,-20000);
50586             this.el.hide();
50587             this.collapsedEl.show();
50588             this.fireEvent("collapsed", this);
50589             this.fireEvent("invalidated", this);
50590         }
50591     },
50592
50593     animateCollapse : function(){
50594         // overridden
50595     },
50596
50597     /**
50598      * Expands this region if it was previously collapsed.
50599      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50600      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50601      */
50602     expand : function(e, skipAnim){
50603         if(e) {
50604             e.stopPropagation();
50605         }
50606         if(!this.collapsed || this.el.hasActiveFx()) {
50607             return;
50608         }
50609         if(this.isSlid){
50610             this.afterSlideIn();
50611             skipAnim = true;
50612         }
50613         this.collapsed = false;
50614         if(this.config.animate && skipAnim !== true){
50615             this.animateExpand();
50616         }else{
50617             this.el.show();
50618             if(this.split){
50619                 this.split.el.show();
50620             }
50621             this.collapsedEl.setLocation(-2000,-2000);
50622             this.collapsedEl.hide();
50623             this.fireEvent("invalidated", this);
50624             this.fireEvent("expanded", this);
50625         }
50626     },
50627
50628     animateExpand : function(){
50629         // overridden
50630     },
50631
50632     initTabs : function()
50633     {
50634         this.bodyEl.setStyle("overflow", "hidden");
50635         var ts = new Roo.TabPanel(
50636                 this.bodyEl.dom,
50637                 {
50638                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50639                     disableTooltips: this.config.disableTabTips,
50640                     toolbar : this.config.toolbar
50641                 }
50642         );
50643         if(this.config.hideTabs){
50644             ts.stripWrap.setDisplayed(false);
50645         }
50646         this.tabs = ts;
50647         ts.resizeTabs = this.config.resizeTabs === true;
50648         ts.minTabWidth = this.config.minTabWidth || 40;
50649         ts.maxTabWidth = this.config.maxTabWidth || 250;
50650         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50651         ts.monitorResize = false;
50652         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50653         ts.bodyEl.addClass('x-layout-tabs-body');
50654         this.panels.each(this.initPanelAsTab, this);
50655     },
50656
50657     initPanelAsTab : function(panel){
50658         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50659                     this.config.closeOnTab && panel.isClosable());
50660         if(panel.tabTip !== undefined){
50661             ti.setTooltip(panel.tabTip);
50662         }
50663         ti.on("activate", function(){
50664               this.setActivePanel(panel);
50665         }, this);
50666         if(this.config.closeOnTab){
50667             ti.on("beforeclose", function(t, e){
50668                 e.cancel = true;
50669                 this.remove(panel);
50670             }, this);
50671         }
50672         return ti;
50673     },
50674
50675     updatePanelTitle : function(panel, title){
50676         if(this.activePanel == panel){
50677             this.updateTitle(title);
50678         }
50679         if(this.tabs){
50680             var ti = this.tabs.getTab(panel.getEl().id);
50681             ti.setText(title);
50682             if(panel.tabTip !== undefined){
50683                 ti.setTooltip(panel.tabTip);
50684             }
50685         }
50686     },
50687
50688     updateTitle : function(title){
50689         if(this.titleTextEl && !this.config.title){
50690             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50691         }
50692     },
50693
50694     setActivePanel : function(panel){
50695         panel = this.getPanel(panel);
50696         if(this.activePanel && this.activePanel != panel){
50697             this.activePanel.setActiveState(false);
50698         }
50699         this.activePanel = panel;
50700         panel.setActiveState(true);
50701         if(this.panelSize){
50702             panel.setSize(this.panelSize.width, this.panelSize.height);
50703         }
50704         if(this.closeBtn){
50705             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50706         }
50707         this.updateTitle(panel.getTitle());
50708         if(this.tabs){
50709             this.fireEvent("invalidated", this);
50710         }
50711         this.fireEvent("panelactivated", this, panel);
50712     },
50713
50714     /**
50715      * Shows the specified panel.
50716      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50717      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50718      */
50719     showPanel : function(panel)
50720     {
50721         panel = this.getPanel(panel);
50722         if(panel){
50723             if(this.tabs){
50724                 var tab = this.tabs.getTab(panel.getEl().id);
50725                 if(tab.isHidden()){
50726                     this.tabs.unhideTab(tab.id);
50727                 }
50728                 tab.activate();
50729             }else{
50730                 this.setActivePanel(panel);
50731             }
50732         }
50733         return panel;
50734     },
50735
50736     /**
50737      * Get the active panel for this region.
50738      * @return {Roo.ContentPanel} The active panel or null
50739      */
50740     getActivePanel : function(){
50741         return this.activePanel;
50742     },
50743
50744     validateVisibility : function(){
50745         if(this.panels.getCount() < 1){
50746             this.updateTitle("&#160;");
50747             this.closeBtn.hide();
50748             this.hide();
50749         }else{
50750             if(!this.isVisible()){
50751                 this.show();
50752             }
50753         }
50754     },
50755
50756     /**
50757      * Adds the passed ContentPanel(s) to this region.
50758      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50759      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50760      */
50761     add : function(panel){
50762         if(arguments.length > 1){
50763             for(var i = 0, len = arguments.length; i < len; i++) {
50764                 this.add(arguments[i]);
50765             }
50766             return null;
50767         }
50768         if(this.hasPanel(panel)){
50769             this.showPanel(panel);
50770             return panel;
50771         }
50772         panel.setRegion(this);
50773         this.panels.add(panel);
50774         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50775             this.bodyEl.dom.appendChild(panel.getEl().dom);
50776             if(panel.background !== true){
50777                 this.setActivePanel(panel);
50778             }
50779             this.fireEvent("paneladded", this, panel);
50780             return panel;
50781         }
50782         if(!this.tabs){
50783             this.initTabs();
50784         }else{
50785             this.initPanelAsTab(panel);
50786         }
50787         if(panel.background !== true){
50788             this.tabs.activate(panel.getEl().id);
50789         }
50790         this.fireEvent("paneladded", this, panel);
50791         return panel;
50792     },
50793
50794     /**
50795      * Hides the tab for the specified panel.
50796      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50797      */
50798     hidePanel : function(panel){
50799         if(this.tabs && (panel = this.getPanel(panel))){
50800             this.tabs.hideTab(panel.getEl().id);
50801         }
50802     },
50803
50804     /**
50805      * Unhides the tab for a previously hidden panel.
50806      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50807      */
50808     unhidePanel : function(panel){
50809         if(this.tabs && (panel = this.getPanel(panel))){
50810             this.tabs.unhideTab(panel.getEl().id);
50811         }
50812     },
50813
50814     clearPanels : function(){
50815         while(this.panels.getCount() > 0){
50816              this.remove(this.panels.first());
50817         }
50818     },
50819
50820     /**
50821      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50822      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50823      * @param {Boolean} preservePanel Overrides the config preservePanel option
50824      * @return {Roo.ContentPanel} The panel that was removed
50825      */
50826     remove : function(panel, preservePanel){
50827         panel = this.getPanel(panel);
50828         if(!panel){
50829             return null;
50830         }
50831         var e = {};
50832         this.fireEvent("beforeremove", this, panel, e);
50833         if(e.cancel === true){
50834             return null;
50835         }
50836         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50837         var panelId = panel.getId();
50838         this.panels.removeKey(panelId);
50839         if(preservePanel){
50840             document.body.appendChild(panel.getEl().dom);
50841         }
50842         if(this.tabs){
50843             this.tabs.removeTab(panel.getEl().id);
50844         }else if (!preservePanel){
50845             this.bodyEl.dom.removeChild(panel.getEl().dom);
50846         }
50847         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50848             var p = this.panels.first();
50849             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50850             tempEl.appendChild(p.getEl().dom);
50851             this.bodyEl.update("");
50852             this.bodyEl.dom.appendChild(p.getEl().dom);
50853             tempEl = null;
50854             this.updateTitle(p.getTitle());
50855             this.tabs = null;
50856             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50857             this.setActivePanel(p);
50858         }
50859         panel.setRegion(null);
50860         if(this.activePanel == panel){
50861             this.activePanel = null;
50862         }
50863         if(this.config.autoDestroy !== false && preservePanel !== true){
50864             try{panel.destroy();}catch(e){}
50865         }
50866         this.fireEvent("panelremoved", this, panel);
50867         return panel;
50868     },
50869
50870     /**
50871      * Returns the TabPanel component used by this region
50872      * @return {Roo.TabPanel}
50873      */
50874     getTabs : function(){
50875         return this.tabs;
50876     },
50877
50878     createTool : function(parentEl, className){
50879         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50880             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50881         btn.addClassOnOver("x-layout-tools-button-over");
50882         return btn;
50883     }
50884 });/*
50885  * Based on:
50886  * Ext JS Library 1.1.1
50887  * Copyright(c) 2006-2007, Ext JS, LLC.
50888  *
50889  * Originally Released Under LGPL - original licence link has changed is not relivant.
50890  *
50891  * Fork - LGPL
50892  * <script type="text/javascript">
50893  */
50894  
50895
50896
50897 /**
50898  * @class Roo.SplitLayoutRegion
50899  * @extends Roo.LayoutRegion
50900  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50901  */
50902 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50903     this.cursor = cursor;
50904     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50905 };
50906
50907 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50908     splitTip : "Drag to resize.",
50909     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50910     useSplitTips : false,
50911
50912     applyConfig : function(config){
50913         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50914         if(config.split){
50915             if(!this.split){
50916                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50917                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50918                 /** The SplitBar for this region 
50919                 * @type Roo.SplitBar */
50920                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50921                 this.split.on("moved", this.onSplitMove, this);
50922                 this.split.useShim = config.useShim === true;
50923                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50924                 if(this.useSplitTips){
50925                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50926                 }
50927                 if(config.collapsible){
50928                     this.split.el.on("dblclick", this.collapse,  this);
50929                 }
50930             }
50931             if(typeof config.minSize != "undefined"){
50932                 this.split.minSize = config.minSize;
50933             }
50934             if(typeof config.maxSize != "undefined"){
50935                 this.split.maxSize = config.maxSize;
50936             }
50937             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50938                 this.hideSplitter();
50939             }
50940         }
50941     },
50942
50943     getHMaxSize : function(){
50944          var cmax = this.config.maxSize || 10000;
50945          var center = this.mgr.getRegion("center");
50946          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50947     },
50948
50949     getVMaxSize : function(){
50950          var cmax = this.config.maxSize || 10000;
50951          var center = this.mgr.getRegion("center");
50952          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50953     },
50954
50955     onSplitMove : function(split, newSize){
50956         this.fireEvent("resized", this, newSize);
50957     },
50958     
50959     /** 
50960      * Returns the {@link Roo.SplitBar} for this region.
50961      * @return {Roo.SplitBar}
50962      */
50963     getSplitBar : function(){
50964         return this.split;
50965     },
50966     
50967     hide : function(){
50968         this.hideSplitter();
50969         Roo.SplitLayoutRegion.superclass.hide.call(this);
50970     },
50971
50972     hideSplitter : function(){
50973         if(this.split){
50974             this.split.el.setLocation(-2000,-2000);
50975             this.split.el.hide();
50976         }
50977     },
50978
50979     show : function(){
50980         if(this.split){
50981             this.split.el.show();
50982         }
50983         Roo.SplitLayoutRegion.superclass.show.call(this);
50984     },
50985     
50986     beforeSlide: function(){
50987         if(Roo.isGecko){// firefox overflow auto bug workaround
50988             this.bodyEl.clip();
50989             if(this.tabs) {
50990                 this.tabs.bodyEl.clip();
50991             }
50992             if(this.activePanel){
50993                 this.activePanel.getEl().clip();
50994                 
50995                 if(this.activePanel.beforeSlide){
50996                     this.activePanel.beforeSlide();
50997                 }
50998             }
50999         }
51000     },
51001     
51002     afterSlide : function(){
51003         if(Roo.isGecko){// firefox overflow auto bug workaround
51004             this.bodyEl.unclip();
51005             if(this.tabs) {
51006                 this.tabs.bodyEl.unclip();
51007             }
51008             if(this.activePanel){
51009                 this.activePanel.getEl().unclip();
51010                 if(this.activePanel.afterSlide){
51011                     this.activePanel.afterSlide();
51012                 }
51013             }
51014         }
51015     },
51016
51017     initAutoHide : function(){
51018         if(this.autoHide !== false){
51019             if(!this.autoHideHd){
51020                 var st = new Roo.util.DelayedTask(this.slideIn, this);
51021                 this.autoHideHd = {
51022                     "mouseout": function(e){
51023                         if(!e.within(this.el, true)){
51024                             st.delay(500);
51025                         }
51026                     },
51027                     "mouseover" : function(e){
51028                         st.cancel();
51029                     },
51030                     scope : this
51031                 };
51032             }
51033             this.el.on(this.autoHideHd);
51034         }
51035     },
51036
51037     clearAutoHide : function(){
51038         if(this.autoHide !== false){
51039             this.el.un("mouseout", this.autoHideHd.mouseout);
51040             this.el.un("mouseover", this.autoHideHd.mouseover);
51041         }
51042     },
51043
51044     clearMonitor : function(){
51045         Roo.get(document).un("click", this.slideInIf, this);
51046     },
51047
51048     // these names are backwards but not changed for compat
51049     slideOut : function(){
51050         if(this.isSlid || this.el.hasActiveFx()){
51051             return;
51052         }
51053         this.isSlid = true;
51054         if(this.collapseBtn){
51055             this.collapseBtn.hide();
51056         }
51057         this.closeBtnState = this.closeBtn.getStyle('display');
51058         this.closeBtn.hide();
51059         if(this.stickBtn){
51060             this.stickBtn.show();
51061         }
51062         this.el.show();
51063         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
51064         this.beforeSlide();
51065         this.el.setStyle("z-index", 10001);
51066         this.el.slideIn(this.getSlideAnchor(), {
51067             callback: function(){
51068                 this.afterSlide();
51069                 this.initAutoHide();
51070                 Roo.get(document).on("click", this.slideInIf, this);
51071                 this.fireEvent("slideshow", this);
51072             },
51073             scope: this,
51074             block: true
51075         });
51076     },
51077
51078     afterSlideIn : function(){
51079         this.clearAutoHide();
51080         this.isSlid = false;
51081         this.clearMonitor();
51082         this.el.setStyle("z-index", "");
51083         if(this.collapseBtn){
51084             this.collapseBtn.show();
51085         }
51086         this.closeBtn.setStyle('display', this.closeBtnState);
51087         if(this.stickBtn){
51088             this.stickBtn.hide();
51089         }
51090         this.fireEvent("slidehide", this);
51091     },
51092
51093     slideIn : function(cb){
51094         if(!this.isSlid || this.el.hasActiveFx()){
51095             Roo.callback(cb);
51096             return;
51097         }
51098         this.isSlid = false;
51099         this.beforeSlide();
51100         this.el.slideOut(this.getSlideAnchor(), {
51101             callback: function(){
51102                 this.el.setLeftTop(-10000, -10000);
51103                 this.afterSlide();
51104                 this.afterSlideIn();
51105                 Roo.callback(cb);
51106             },
51107             scope: this,
51108             block: true
51109         });
51110     },
51111     
51112     slideInIf : function(e){
51113         if(!e.within(this.el)){
51114             this.slideIn();
51115         }
51116     },
51117
51118     animateCollapse : function(){
51119         this.beforeSlide();
51120         this.el.setStyle("z-index", 20000);
51121         var anchor = this.getSlideAnchor();
51122         this.el.slideOut(anchor, {
51123             callback : function(){
51124                 this.el.setStyle("z-index", "");
51125                 this.collapsedEl.slideIn(anchor, {duration:.3});
51126                 this.afterSlide();
51127                 this.el.setLocation(-10000,-10000);
51128                 this.el.hide();
51129                 this.fireEvent("collapsed", this);
51130             },
51131             scope: this,
51132             block: true
51133         });
51134     },
51135
51136     animateExpand : function(){
51137         this.beforeSlide();
51138         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51139         this.el.setStyle("z-index", 20000);
51140         this.collapsedEl.hide({
51141             duration:.1
51142         });
51143         this.el.slideIn(this.getSlideAnchor(), {
51144             callback : function(){
51145                 this.el.setStyle("z-index", "");
51146                 this.afterSlide();
51147                 if(this.split){
51148                     this.split.el.show();
51149                 }
51150                 this.fireEvent("invalidated", this);
51151                 this.fireEvent("expanded", this);
51152             },
51153             scope: this,
51154             block: true
51155         });
51156     },
51157
51158     anchors : {
51159         "west" : "left",
51160         "east" : "right",
51161         "north" : "top",
51162         "south" : "bottom"
51163     },
51164
51165     sanchors : {
51166         "west" : "l",
51167         "east" : "r",
51168         "north" : "t",
51169         "south" : "b"
51170     },
51171
51172     canchors : {
51173         "west" : "tl-tr",
51174         "east" : "tr-tl",
51175         "north" : "tl-bl",
51176         "south" : "bl-tl"
51177     },
51178
51179     getAnchor : function(){
51180         return this.anchors[this.position];
51181     },
51182
51183     getCollapseAnchor : function(){
51184         return this.canchors[this.position];
51185     },
51186
51187     getSlideAnchor : function(){
51188         return this.sanchors[this.position];
51189     },
51190
51191     getAlignAdj : function(){
51192         var cm = this.cmargins;
51193         switch(this.position){
51194             case "west":
51195                 return [0, 0];
51196             break;
51197             case "east":
51198                 return [0, 0];
51199             break;
51200             case "north":
51201                 return [0, 0];
51202             break;
51203             case "south":
51204                 return [0, 0];
51205             break;
51206         }
51207     },
51208
51209     getExpandAdj : function(){
51210         var c = this.collapsedEl, cm = this.cmargins;
51211         switch(this.position){
51212             case "west":
51213                 return [-(cm.right+c.getWidth()+cm.left), 0];
51214             break;
51215             case "east":
51216                 return [cm.right+c.getWidth()+cm.left, 0];
51217             break;
51218             case "north":
51219                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51220             break;
51221             case "south":
51222                 return [0, cm.top+cm.bottom+c.getHeight()];
51223             break;
51224         }
51225     }
51226 });/*
51227  * Based on:
51228  * Ext JS Library 1.1.1
51229  * Copyright(c) 2006-2007, Ext JS, LLC.
51230  *
51231  * Originally Released Under LGPL - original licence link has changed is not relivant.
51232  *
51233  * Fork - LGPL
51234  * <script type="text/javascript">
51235  */
51236 /*
51237  * These classes are private internal classes
51238  */
51239 Roo.CenterLayoutRegion = function(mgr, config){
51240     Roo.LayoutRegion.call(this, mgr, config, "center");
51241     this.visible = true;
51242     this.minWidth = config.minWidth || 20;
51243     this.minHeight = config.minHeight || 20;
51244 };
51245
51246 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51247     hide : function(){
51248         // center panel can't be hidden
51249     },
51250     
51251     show : function(){
51252         // center panel can't be hidden
51253     },
51254     
51255     getMinWidth: function(){
51256         return this.minWidth;
51257     },
51258     
51259     getMinHeight: function(){
51260         return this.minHeight;
51261     }
51262 });
51263
51264
51265 Roo.NorthLayoutRegion = function(mgr, config){
51266     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51267     if(this.split){
51268         this.split.placement = Roo.SplitBar.TOP;
51269         this.split.orientation = Roo.SplitBar.VERTICAL;
51270         this.split.el.addClass("x-layout-split-v");
51271     }
51272     var size = config.initialSize || config.height;
51273     if(typeof size != "undefined"){
51274         this.el.setHeight(size);
51275     }
51276 };
51277 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51278     orientation: Roo.SplitBar.VERTICAL,
51279     getBox : function(){
51280         if(this.collapsed){
51281             return this.collapsedEl.getBox();
51282         }
51283         var box = this.el.getBox();
51284         if(this.split){
51285             box.height += this.split.el.getHeight();
51286         }
51287         return box;
51288     },
51289     
51290     updateBox : function(box){
51291         if(this.split && !this.collapsed){
51292             box.height -= this.split.el.getHeight();
51293             this.split.el.setLeft(box.x);
51294             this.split.el.setTop(box.y+box.height);
51295             this.split.el.setWidth(box.width);
51296         }
51297         if(this.collapsed){
51298             this.updateBody(box.width, null);
51299         }
51300         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51301     }
51302 });
51303
51304 Roo.SouthLayoutRegion = function(mgr, config){
51305     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51306     if(this.split){
51307         this.split.placement = Roo.SplitBar.BOTTOM;
51308         this.split.orientation = Roo.SplitBar.VERTICAL;
51309         this.split.el.addClass("x-layout-split-v");
51310     }
51311     var size = config.initialSize || config.height;
51312     if(typeof size != "undefined"){
51313         this.el.setHeight(size);
51314     }
51315 };
51316 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51317     orientation: Roo.SplitBar.VERTICAL,
51318     getBox : function(){
51319         if(this.collapsed){
51320             return this.collapsedEl.getBox();
51321         }
51322         var box = this.el.getBox();
51323         if(this.split){
51324             var sh = this.split.el.getHeight();
51325             box.height += sh;
51326             box.y -= sh;
51327         }
51328         return box;
51329     },
51330     
51331     updateBox : function(box){
51332         if(this.split && !this.collapsed){
51333             var sh = this.split.el.getHeight();
51334             box.height -= sh;
51335             box.y += sh;
51336             this.split.el.setLeft(box.x);
51337             this.split.el.setTop(box.y-sh);
51338             this.split.el.setWidth(box.width);
51339         }
51340         if(this.collapsed){
51341             this.updateBody(box.width, null);
51342         }
51343         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51344     }
51345 });
51346
51347 Roo.EastLayoutRegion = function(mgr, config){
51348     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51349     if(this.split){
51350         this.split.placement = Roo.SplitBar.RIGHT;
51351         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51352         this.split.el.addClass("x-layout-split-h");
51353     }
51354     var size = config.initialSize || config.width;
51355     if(typeof size != "undefined"){
51356         this.el.setWidth(size);
51357     }
51358 };
51359 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51360     orientation: Roo.SplitBar.HORIZONTAL,
51361     getBox : function(){
51362         if(this.collapsed){
51363             return this.collapsedEl.getBox();
51364         }
51365         var box = this.el.getBox();
51366         if(this.split){
51367             var sw = this.split.el.getWidth();
51368             box.width += sw;
51369             box.x -= sw;
51370         }
51371         return box;
51372     },
51373
51374     updateBox : function(box){
51375         if(this.split && !this.collapsed){
51376             var sw = this.split.el.getWidth();
51377             box.width -= sw;
51378             this.split.el.setLeft(box.x);
51379             this.split.el.setTop(box.y);
51380             this.split.el.setHeight(box.height);
51381             box.x += sw;
51382         }
51383         if(this.collapsed){
51384             this.updateBody(null, box.height);
51385         }
51386         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51387     }
51388 });
51389
51390 Roo.WestLayoutRegion = function(mgr, config){
51391     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51392     if(this.split){
51393         this.split.placement = Roo.SplitBar.LEFT;
51394         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51395         this.split.el.addClass("x-layout-split-h");
51396     }
51397     var size = config.initialSize || config.width;
51398     if(typeof size != "undefined"){
51399         this.el.setWidth(size);
51400     }
51401 };
51402 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51403     orientation: Roo.SplitBar.HORIZONTAL,
51404     getBox : function(){
51405         if(this.collapsed){
51406             return this.collapsedEl.getBox();
51407         }
51408         var box = this.el.getBox();
51409         if(this.split){
51410             box.width += this.split.el.getWidth();
51411         }
51412         return box;
51413     },
51414     
51415     updateBox : function(box){
51416         if(this.split && !this.collapsed){
51417             var sw = this.split.el.getWidth();
51418             box.width -= sw;
51419             this.split.el.setLeft(box.x+box.width);
51420             this.split.el.setTop(box.y);
51421             this.split.el.setHeight(box.height);
51422         }
51423         if(this.collapsed){
51424             this.updateBody(null, box.height);
51425         }
51426         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51427     }
51428 });
51429 /*
51430  * Based on:
51431  * Ext JS Library 1.1.1
51432  * Copyright(c) 2006-2007, Ext JS, LLC.
51433  *
51434  * Originally Released Under LGPL - original licence link has changed is not relivant.
51435  *
51436  * Fork - LGPL
51437  * <script type="text/javascript">
51438  */
51439  
51440  
51441 /*
51442  * Private internal class for reading and applying state
51443  */
51444 Roo.LayoutStateManager = function(layout){
51445      // default empty state
51446      this.state = {
51447         north: {},
51448         south: {},
51449         east: {},
51450         west: {}       
51451     };
51452 };
51453
51454 Roo.LayoutStateManager.prototype = {
51455     init : function(layout, provider){
51456         this.provider = provider;
51457         var state = provider.get(layout.id+"-layout-state");
51458         if(state){
51459             var wasUpdating = layout.isUpdating();
51460             if(!wasUpdating){
51461                 layout.beginUpdate();
51462             }
51463             for(var key in state){
51464                 if(typeof state[key] != "function"){
51465                     var rstate = state[key];
51466                     var r = layout.getRegion(key);
51467                     if(r && rstate){
51468                         if(rstate.size){
51469                             r.resizeTo(rstate.size);
51470                         }
51471                         if(rstate.collapsed == true){
51472                             r.collapse(true);
51473                         }else{
51474                             r.expand(null, true);
51475                         }
51476                     }
51477                 }
51478             }
51479             if(!wasUpdating){
51480                 layout.endUpdate();
51481             }
51482             this.state = state; 
51483         }
51484         this.layout = layout;
51485         layout.on("regionresized", this.onRegionResized, this);
51486         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51487         layout.on("regionexpanded", this.onRegionExpanded, this);
51488     },
51489     
51490     storeState : function(){
51491         this.provider.set(this.layout.id+"-layout-state", this.state);
51492     },
51493     
51494     onRegionResized : function(region, newSize){
51495         this.state[region.getPosition()].size = newSize;
51496         this.storeState();
51497     },
51498     
51499     onRegionCollapsed : function(region){
51500         this.state[region.getPosition()].collapsed = true;
51501         this.storeState();
51502     },
51503     
51504     onRegionExpanded : function(region){
51505         this.state[region.getPosition()].collapsed = false;
51506         this.storeState();
51507     }
51508 };/*
51509  * Based on:
51510  * Ext JS Library 1.1.1
51511  * Copyright(c) 2006-2007, Ext JS, LLC.
51512  *
51513  * Originally Released Under LGPL - original licence link has changed is not relivant.
51514  *
51515  * Fork - LGPL
51516  * <script type="text/javascript">
51517  */
51518 /**
51519  * @class Roo.ContentPanel
51520  * @extends Roo.util.Observable
51521  * A basic ContentPanel element.
51522  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51523  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51524  * @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
51525  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51526  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51527  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51528  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51529  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51530  * @cfg {String} title          The title for this panel
51531  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51532  * @cfg {String} url            Calls {@link #setUrl} with this value
51533  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51534  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51535  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51536  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51537
51538  * @constructor
51539  * Create a new ContentPanel.
51540  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51541  * @param {String/Object} config A string to set only the title or a config object
51542  * @param {String} content (optional) Set the HTML content for this panel
51543  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51544  */
51545 Roo.ContentPanel = function(el, config, content){
51546     
51547      
51548     /*
51549     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51550         config = el;
51551         el = Roo.id();
51552     }
51553     if (config && config.parentLayout) { 
51554         el = config.parentLayout.el.createChild(); 
51555     }
51556     */
51557     if(el.autoCreate){ // xtype is available if this is called from factory
51558         config = el;
51559         el = Roo.id();
51560     }
51561     this.el = Roo.get(el);
51562     if(!this.el && config && config.autoCreate){
51563         if(typeof config.autoCreate == "object"){
51564             if(!config.autoCreate.id){
51565                 config.autoCreate.id = config.id||el;
51566             }
51567             this.el = Roo.DomHelper.append(document.body,
51568                         config.autoCreate, true);
51569         }else{
51570             this.el = Roo.DomHelper.append(document.body,
51571                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51572         }
51573     }
51574     this.closable = false;
51575     this.loaded = false;
51576     this.active = false;
51577     if(typeof config == "string"){
51578         this.title = config;
51579     }else{
51580         Roo.apply(this, config);
51581     }
51582     
51583     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51584         this.wrapEl = this.el.wrap();
51585         this.toolbar.container = this.el.insertSibling(false, 'before');
51586         this.toolbar = new Roo.Toolbar(this.toolbar);
51587     }
51588     
51589     // xtype created footer. - not sure if will work as we normally have to render first..
51590     if (this.footer && !this.footer.el && this.footer.xtype) {
51591         if (!this.wrapEl) {
51592             this.wrapEl = this.el.wrap();
51593         }
51594     
51595         this.footer.container = this.wrapEl.createChild();
51596          
51597         this.footer = Roo.factory(this.footer, Roo);
51598         
51599     }
51600     
51601     if(this.resizeEl){
51602         this.resizeEl = Roo.get(this.resizeEl, true);
51603     }else{
51604         this.resizeEl = this.el;
51605     }
51606     // handle view.xtype
51607     
51608  
51609     
51610     
51611     this.addEvents({
51612         /**
51613          * @event activate
51614          * Fires when this panel is activated. 
51615          * @param {Roo.ContentPanel} this
51616          */
51617         "activate" : true,
51618         /**
51619          * @event deactivate
51620          * Fires when this panel is activated. 
51621          * @param {Roo.ContentPanel} this
51622          */
51623         "deactivate" : true,
51624
51625         /**
51626          * @event resize
51627          * Fires when this panel is resized if fitToFrame is true.
51628          * @param {Roo.ContentPanel} this
51629          * @param {Number} width The width after any component adjustments
51630          * @param {Number} height The height after any component adjustments
51631          */
51632         "resize" : true,
51633         
51634          /**
51635          * @event render
51636          * Fires when this tab is created
51637          * @param {Roo.ContentPanel} this
51638          */
51639         "render" : true
51640         
51641         
51642         
51643     });
51644     
51645
51646     
51647     
51648     if(this.autoScroll){
51649         this.resizeEl.setStyle("overflow", "auto");
51650     } else {
51651         // fix randome scrolling
51652         this.el.on('scroll', function() {
51653             Roo.log('fix random scolling');
51654             this.scrollTo('top',0); 
51655         });
51656     }
51657     content = content || this.content;
51658     if(content){
51659         this.setContent(content);
51660     }
51661     if(config && config.url){
51662         this.setUrl(this.url, this.params, this.loadOnce);
51663     }
51664     
51665     
51666     
51667     Roo.ContentPanel.superclass.constructor.call(this);
51668     
51669     if (this.view && typeof(this.view.xtype) != 'undefined') {
51670         this.view.el = this.el.appendChild(document.createElement("div"));
51671         this.view = Roo.factory(this.view); 
51672         this.view.render  &&  this.view.render(false, '');  
51673     }
51674     
51675     
51676     this.fireEvent('render', this);
51677 };
51678
51679 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51680     tabTip:'',
51681     setRegion : function(region){
51682         this.region = region;
51683         if(region){
51684            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51685         }else{
51686            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51687         } 
51688     },
51689     
51690     /**
51691      * Returns the toolbar for this Panel if one was configured. 
51692      * @return {Roo.Toolbar} 
51693      */
51694     getToolbar : function(){
51695         return this.toolbar;
51696     },
51697     
51698     setActiveState : function(active){
51699         this.active = active;
51700         if(!active){
51701             this.fireEvent("deactivate", this);
51702         }else{
51703             this.fireEvent("activate", this);
51704         }
51705     },
51706     /**
51707      * Updates this panel's element
51708      * @param {String} content The new content
51709      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51710     */
51711     setContent : function(content, loadScripts){
51712         this.el.update(content, loadScripts);
51713     },
51714
51715     ignoreResize : function(w, h){
51716         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51717             return true;
51718         }else{
51719             this.lastSize = {width: w, height: h};
51720             return false;
51721         }
51722     },
51723     /**
51724      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51725      * @return {Roo.UpdateManager} The UpdateManager
51726      */
51727     getUpdateManager : function(){
51728         return this.el.getUpdateManager();
51729     },
51730      /**
51731      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51732      * @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:
51733 <pre><code>
51734 panel.load({
51735     url: "your-url.php",
51736     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51737     callback: yourFunction,
51738     scope: yourObject, //(optional scope)
51739     discardUrl: false,
51740     nocache: false,
51741     text: "Loading...",
51742     timeout: 30,
51743     scripts: false
51744 });
51745 </code></pre>
51746      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51747      * 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.
51748      * @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}
51749      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51750      * @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.
51751      * @return {Roo.ContentPanel} this
51752      */
51753     load : function(){
51754         var um = this.el.getUpdateManager();
51755         um.update.apply(um, arguments);
51756         return this;
51757     },
51758
51759
51760     /**
51761      * 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.
51762      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51763      * @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)
51764      * @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)
51765      * @return {Roo.UpdateManager} The UpdateManager
51766      */
51767     setUrl : function(url, params, loadOnce){
51768         if(this.refreshDelegate){
51769             this.removeListener("activate", this.refreshDelegate);
51770         }
51771         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51772         this.on("activate", this.refreshDelegate);
51773         return this.el.getUpdateManager();
51774     },
51775     
51776     _handleRefresh : function(url, params, loadOnce){
51777         if(!loadOnce || !this.loaded){
51778             var updater = this.el.getUpdateManager();
51779             updater.update(url, params, this._setLoaded.createDelegate(this));
51780         }
51781     },
51782     
51783     _setLoaded : function(){
51784         this.loaded = true;
51785     }, 
51786     
51787     /**
51788      * Returns this panel's id
51789      * @return {String} 
51790      */
51791     getId : function(){
51792         return this.el.id;
51793     },
51794     
51795     /** 
51796      * Returns this panel's element - used by regiosn to add.
51797      * @return {Roo.Element} 
51798      */
51799     getEl : function(){
51800         return this.wrapEl || this.el;
51801     },
51802     
51803     adjustForComponents : function(width, height)
51804     {
51805         //Roo.log('adjustForComponents ');
51806         if(this.resizeEl != this.el){
51807             width -= this.el.getFrameWidth('lr');
51808             height -= this.el.getFrameWidth('tb');
51809         }
51810         if(this.toolbar){
51811             var te = this.toolbar.getEl();
51812             height -= te.getHeight();
51813             te.setWidth(width);
51814         }
51815         if(this.footer){
51816             var te = this.footer.getEl();
51817             Roo.log("footer:" + te.getHeight());
51818             
51819             height -= te.getHeight();
51820             te.setWidth(width);
51821         }
51822         
51823         
51824         if(this.adjustments){
51825             width += this.adjustments[0];
51826             height += this.adjustments[1];
51827         }
51828         return {"width": width, "height": height};
51829     },
51830     
51831     setSize : function(width, height){
51832         if(this.fitToFrame && !this.ignoreResize(width, height)){
51833             if(this.fitContainer && this.resizeEl != this.el){
51834                 this.el.setSize(width, height);
51835             }
51836             var size = this.adjustForComponents(width, height);
51837             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51838             this.fireEvent('resize', this, size.width, size.height);
51839         }
51840     },
51841     
51842     /**
51843      * Returns this panel's title
51844      * @return {String} 
51845      */
51846     getTitle : function(){
51847         return this.title;
51848     },
51849     
51850     /**
51851      * Set this panel's title
51852      * @param {String} title
51853      */
51854     setTitle : function(title){
51855         this.title = title;
51856         if(this.region){
51857             this.region.updatePanelTitle(this, title);
51858         }
51859     },
51860     
51861     /**
51862      * Returns true is this panel was configured to be closable
51863      * @return {Boolean} 
51864      */
51865     isClosable : function(){
51866         return this.closable;
51867     },
51868     
51869     beforeSlide : function(){
51870         this.el.clip();
51871         this.resizeEl.clip();
51872     },
51873     
51874     afterSlide : function(){
51875         this.el.unclip();
51876         this.resizeEl.unclip();
51877     },
51878     
51879     /**
51880      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51881      *   Will fail silently if the {@link #setUrl} method has not been called.
51882      *   This does not activate the panel, just updates its content.
51883      */
51884     refresh : function(){
51885         if(this.refreshDelegate){
51886            this.loaded = false;
51887            this.refreshDelegate();
51888         }
51889     },
51890     
51891     /**
51892      * Destroys this panel
51893      */
51894     destroy : function(){
51895         this.el.removeAllListeners();
51896         var tempEl = document.createElement("span");
51897         tempEl.appendChild(this.el.dom);
51898         tempEl.innerHTML = "";
51899         this.el.remove();
51900         this.el = null;
51901     },
51902     
51903     /**
51904      * form - if the content panel contains a form - this is a reference to it.
51905      * @type {Roo.form.Form}
51906      */
51907     form : false,
51908     /**
51909      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51910      *    This contains a reference to it.
51911      * @type {Roo.View}
51912      */
51913     view : false,
51914     
51915       /**
51916      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51917      * <pre><code>
51918
51919 layout.addxtype({
51920        xtype : 'Form',
51921        items: [ .... ]
51922    }
51923 );
51924
51925 </code></pre>
51926      * @param {Object} cfg Xtype definition of item to add.
51927      */
51928     
51929     addxtype : function(cfg) {
51930         // add form..
51931         if (cfg.xtype.match(/^Form$/)) {
51932             
51933             var el;
51934             //if (this.footer) {
51935             //    el = this.footer.container.insertSibling(false, 'before');
51936             //} else {
51937                 el = this.el.createChild();
51938             //}
51939
51940             this.form = new  Roo.form.Form(cfg);
51941             
51942             
51943             if ( this.form.allItems.length) {
51944                 this.form.render(el.dom);
51945             }
51946             return this.form;
51947         }
51948         // should only have one of theses..
51949         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51950             // views.. should not be just added - used named prop 'view''
51951             
51952             cfg.el = this.el.appendChild(document.createElement("div"));
51953             // factory?
51954             
51955             var ret = new Roo.factory(cfg);
51956              
51957              ret.render && ret.render(false, ''); // render blank..
51958             this.view = ret;
51959             return ret;
51960         }
51961         return false;
51962     }
51963 });
51964
51965 /**
51966  * @class Roo.GridPanel
51967  * @extends Roo.ContentPanel
51968  * @constructor
51969  * Create a new GridPanel.
51970  * @param {Roo.grid.Grid} grid The grid for this panel
51971  * @param {String/Object} config A string to set only the panel's title, or a config object
51972  */
51973 Roo.GridPanel = function(grid, config){
51974     
51975   
51976     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51977         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51978         
51979     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51980     
51981     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51982     
51983     if(this.toolbar){
51984         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51985     }
51986     // xtype created footer. - not sure if will work as we normally have to render first..
51987     if (this.footer && !this.footer.el && this.footer.xtype) {
51988         
51989         this.footer.container = this.grid.getView().getFooterPanel(true);
51990         this.footer.dataSource = this.grid.dataSource;
51991         this.footer = Roo.factory(this.footer, Roo);
51992         
51993     }
51994     
51995     grid.monitorWindowResize = false; // turn off autosizing
51996     grid.autoHeight = false;
51997     grid.autoWidth = false;
51998     this.grid = grid;
51999     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
52000 };
52001
52002 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
52003     getId : function(){
52004         return this.grid.id;
52005     },
52006     
52007     /**
52008      * Returns the grid for this panel
52009      * @return {Roo.grid.Grid} 
52010      */
52011     getGrid : function(){
52012         return this.grid;    
52013     },
52014     
52015     setSize : function(width, height){
52016         if(!this.ignoreResize(width, height)){
52017             var grid = this.grid;
52018             var size = this.adjustForComponents(width, height);
52019             grid.getGridEl().setSize(size.width, size.height);
52020             grid.autoSize();
52021         }
52022     },
52023     
52024     beforeSlide : function(){
52025         this.grid.getView().scroller.clip();
52026     },
52027     
52028     afterSlide : function(){
52029         this.grid.getView().scroller.unclip();
52030     },
52031     
52032     destroy : function(){
52033         this.grid.destroy();
52034         delete this.grid;
52035         Roo.GridPanel.superclass.destroy.call(this); 
52036     }
52037 });
52038
52039
52040 /**
52041  * @class Roo.NestedLayoutPanel
52042  * @extends Roo.ContentPanel
52043  * @constructor
52044  * Create a new NestedLayoutPanel.
52045  * 
52046  * 
52047  * @param {Roo.BorderLayout} layout The layout for this panel
52048  * @param {String/Object} config A string to set only the title or a config object
52049  */
52050 Roo.NestedLayoutPanel = function(layout, config)
52051 {
52052     // construct with only one argument..
52053     /* FIXME - implement nicer consturctors
52054     if (layout.layout) {
52055         config = layout;
52056         layout = config.layout;
52057         delete config.layout;
52058     }
52059     if (layout.xtype && !layout.getEl) {
52060         // then layout needs constructing..
52061         layout = Roo.factory(layout, Roo);
52062     }
52063     */
52064     
52065     
52066     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
52067     
52068     layout.monitorWindowResize = false; // turn off autosizing
52069     this.layout = layout;
52070     this.layout.getEl().addClass("x-layout-nested-layout");
52071     
52072     
52073     
52074     
52075 };
52076
52077 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
52078
52079     setSize : function(width, height){
52080         if(!this.ignoreResize(width, height)){
52081             var size = this.adjustForComponents(width, height);
52082             var el = this.layout.getEl();
52083             el.setSize(size.width, size.height);
52084             var touch = el.dom.offsetWidth;
52085             this.layout.layout();
52086             // ie requires a double layout on the first pass
52087             if(Roo.isIE && !this.initialized){
52088                 this.initialized = true;
52089                 this.layout.layout();
52090             }
52091         }
52092     },
52093     
52094     // activate all subpanels if not currently active..
52095     
52096     setActiveState : function(active){
52097         this.active = active;
52098         if(!active){
52099             this.fireEvent("deactivate", this);
52100             return;
52101         }
52102         
52103         this.fireEvent("activate", this);
52104         // not sure if this should happen before or after..
52105         if (!this.layout) {
52106             return; // should not happen..
52107         }
52108         var reg = false;
52109         for (var r in this.layout.regions) {
52110             reg = this.layout.getRegion(r);
52111             if (reg.getActivePanel()) {
52112                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
52113                 reg.setActivePanel(reg.getActivePanel());
52114                 continue;
52115             }
52116             if (!reg.panels.length) {
52117                 continue;
52118             }
52119             reg.showPanel(reg.getPanel(0));
52120         }
52121         
52122         
52123         
52124         
52125     },
52126     
52127     /**
52128      * Returns the nested BorderLayout for this panel
52129      * @return {Roo.BorderLayout} 
52130      */
52131     getLayout : function(){
52132         return this.layout;
52133     },
52134     
52135      /**
52136      * Adds a xtype elements to the layout of the nested panel
52137      * <pre><code>
52138
52139 panel.addxtype({
52140        xtype : 'ContentPanel',
52141        region: 'west',
52142        items: [ .... ]
52143    }
52144 );
52145
52146 panel.addxtype({
52147         xtype : 'NestedLayoutPanel',
52148         region: 'west',
52149         layout: {
52150            center: { },
52151            west: { }   
52152         },
52153         items : [ ... list of content panels or nested layout panels.. ]
52154    }
52155 );
52156 </code></pre>
52157      * @param {Object} cfg Xtype definition of item to add.
52158      */
52159     addxtype : function(cfg) {
52160         return this.layout.addxtype(cfg);
52161     
52162     }
52163 });
52164
52165 Roo.ScrollPanel = function(el, config, content){
52166     config = config || {};
52167     config.fitToFrame = true;
52168     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52169     
52170     this.el.dom.style.overflow = "hidden";
52171     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52172     this.el.removeClass("x-layout-inactive-content");
52173     this.el.on("mousewheel", this.onWheel, this);
52174
52175     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52176     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52177     up.unselectable(); down.unselectable();
52178     up.on("click", this.scrollUp, this);
52179     down.on("click", this.scrollDown, this);
52180     up.addClassOnOver("x-scroller-btn-over");
52181     down.addClassOnOver("x-scroller-btn-over");
52182     up.addClassOnClick("x-scroller-btn-click");
52183     down.addClassOnClick("x-scroller-btn-click");
52184     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52185
52186     this.resizeEl = this.el;
52187     this.el = wrap; this.up = up; this.down = down;
52188 };
52189
52190 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52191     increment : 100,
52192     wheelIncrement : 5,
52193     scrollUp : function(){
52194         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52195     },
52196
52197     scrollDown : function(){
52198         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52199     },
52200
52201     afterScroll : function(){
52202         var el = this.resizeEl;
52203         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52204         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52205         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52206     },
52207
52208     setSize : function(){
52209         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52210         this.afterScroll();
52211     },
52212
52213     onWheel : function(e){
52214         var d = e.getWheelDelta();
52215         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52216         this.afterScroll();
52217         e.stopEvent();
52218     },
52219
52220     setContent : function(content, loadScripts){
52221         this.resizeEl.update(content, loadScripts);
52222     }
52223
52224 });
52225
52226
52227
52228
52229
52230
52231
52232
52233
52234 /**
52235  * @class Roo.TreePanel
52236  * @extends Roo.ContentPanel
52237  * @constructor
52238  * Create a new TreePanel. - defaults to fit/scoll contents.
52239  * @param {String/Object} config A string to set only the panel's title, or a config object
52240  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52241  */
52242 Roo.TreePanel = function(config){
52243     var el = config.el;
52244     var tree = config.tree;
52245     delete config.tree; 
52246     delete config.el; // hopefull!
52247     
52248     // wrapper for IE7 strict & safari scroll issue
52249     
52250     var treeEl = el.createChild();
52251     config.resizeEl = treeEl;
52252     
52253     
52254     
52255     Roo.TreePanel.superclass.constructor.call(this, el, config);
52256  
52257  
52258     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52259     //console.log(tree);
52260     this.on('activate', function()
52261     {
52262         if (this.tree.rendered) {
52263             return;
52264         }
52265         //console.log('render tree');
52266         this.tree.render();
52267     });
52268     // this should not be needed.. - it's actually the 'el' that resizes?
52269     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52270     
52271     //this.on('resize',  function (cp, w, h) {
52272     //        this.tree.innerCt.setWidth(w);
52273     //        this.tree.innerCt.setHeight(h);
52274     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52275     //});
52276
52277         
52278     
52279 };
52280
52281 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52282     fitToFrame : true,
52283     autoScroll : true
52284 });
52285
52286
52287
52288
52289
52290
52291
52292
52293
52294
52295
52296 /*
52297  * Based on:
52298  * Ext JS Library 1.1.1
52299  * Copyright(c) 2006-2007, Ext JS, LLC.
52300  *
52301  * Originally Released Under LGPL - original licence link has changed is not relivant.
52302  *
52303  * Fork - LGPL
52304  * <script type="text/javascript">
52305  */
52306  
52307
52308 /**
52309  * @class Roo.ReaderLayout
52310  * @extends Roo.BorderLayout
52311  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52312  * center region containing two nested regions (a top one for a list view and one for item preview below),
52313  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52314  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52315  * expedites the setup of the overall layout and regions for this common application style.
52316  * Example:
52317  <pre><code>
52318 var reader = new Roo.ReaderLayout();
52319 var CP = Roo.ContentPanel;  // shortcut for adding
52320
52321 reader.beginUpdate();
52322 reader.add("north", new CP("north", "North"));
52323 reader.add("west", new CP("west", {title: "West"}));
52324 reader.add("east", new CP("east", {title: "East"}));
52325
52326 reader.regions.listView.add(new CP("listView", "List"));
52327 reader.regions.preview.add(new CP("preview", "Preview"));
52328 reader.endUpdate();
52329 </code></pre>
52330 * @constructor
52331 * Create a new ReaderLayout
52332 * @param {Object} config Configuration options
52333 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52334 * document.body if omitted)
52335 */
52336 Roo.ReaderLayout = function(config, renderTo){
52337     var c = config || {size:{}};
52338     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52339         north: c.north !== false ? Roo.apply({
52340             split:false,
52341             initialSize: 32,
52342             titlebar: false
52343         }, c.north) : false,
52344         west: c.west !== false ? Roo.apply({
52345             split:true,
52346             initialSize: 200,
52347             minSize: 175,
52348             maxSize: 400,
52349             titlebar: true,
52350             collapsible: true,
52351             animate: true,
52352             margins:{left:5,right:0,bottom:5,top:5},
52353             cmargins:{left:5,right:5,bottom:5,top:5}
52354         }, c.west) : false,
52355         east: c.east !== false ? Roo.apply({
52356             split:true,
52357             initialSize: 200,
52358             minSize: 175,
52359             maxSize: 400,
52360             titlebar: true,
52361             collapsible: true,
52362             animate: true,
52363             margins:{left:0,right:5,bottom:5,top:5},
52364             cmargins:{left:5,right:5,bottom:5,top:5}
52365         }, c.east) : false,
52366         center: Roo.apply({
52367             tabPosition: 'top',
52368             autoScroll:false,
52369             closeOnTab: true,
52370             titlebar:false,
52371             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52372         }, c.center)
52373     });
52374
52375     this.el.addClass('x-reader');
52376
52377     this.beginUpdate();
52378
52379     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52380         south: c.preview !== false ? Roo.apply({
52381             split:true,
52382             initialSize: 200,
52383             minSize: 100,
52384             autoScroll:true,
52385             collapsible:true,
52386             titlebar: true,
52387             cmargins:{top:5,left:0, right:0, bottom:0}
52388         }, c.preview) : false,
52389         center: Roo.apply({
52390             autoScroll:false,
52391             titlebar:false,
52392             minHeight:200
52393         }, c.listView)
52394     });
52395     this.add('center', new Roo.NestedLayoutPanel(inner,
52396             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52397
52398     this.endUpdate();
52399
52400     this.regions.preview = inner.getRegion('south');
52401     this.regions.listView = inner.getRegion('center');
52402 };
52403
52404 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52405  * Based on:
52406  * Ext JS Library 1.1.1
52407  * Copyright(c) 2006-2007, Ext JS, LLC.
52408  *
52409  * Originally Released Under LGPL - original licence link has changed is not relivant.
52410  *
52411  * Fork - LGPL
52412  * <script type="text/javascript">
52413  */
52414  
52415 /**
52416  * @class Roo.grid.Grid
52417  * @extends Roo.util.Observable
52418  * This class represents the primary interface of a component based grid control.
52419  * <br><br>Usage:<pre><code>
52420  var grid = new Roo.grid.Grid("my-container-id", {
52421      ds: myDataStore,
52422      cm: myColModel,
52423      selModel: mySelectionModel,
52424      autoSizeColumns: true,
52425      monitorWindowResize: false,
52426      trackMouseOver: true
52427  });
52428  // set any options
52429  grid.render();
52430  * </code></pre>
52431  * <b>Common Problems:</b><br/>
52432  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52433  * element will correct this<br/>
52434  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52435  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52436  * are unpredictable.<br/>
52437  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52438  * grid to calculate dimensions/offsets.<br/>
52439   * @constructor
52440  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52441  * The container MUST have some type of size defined for the grid to fill. The container will be
52442  * automatically set to position relative if it isn't already.
52443  * @param {Object} config A config object that sets properties on this grid.
52444  */
52445 Roo.grid.Grid = function(container, config){
52446         // initialize the container
52447         this.container = Roo.get(container);
52448         this.container.update("");
52449         this.container.setStyle("overflow", "hidden");
52450     this.container.addClass('x-grid-container');
52451
52452     this.id = this.container.id;
52453
52454     Roo.apply(this, config);
52455     // check and correct shorthanded configs
52456     if(this.ds){
52457         this.dataSource = this.ds;
52458         delete this.ds;
52459     }
52460     if(this.cm){
52461         this.colModel = this.cm;
52462         delete this.cm;
52463     }
52464     if(this.sm){
52465         this.selModel = this.sm;
52466         delete this.sm;
52467     }
52468
52469     if (this.selModel) {
52470         this.selModel = Roo.factory(this.selModel, Roo.grid);
52471         this.sm = this.selModel;
52472         this.sm.xmodule = this.xmodule || false;
52473     }
52474     if (typeof(this.colModel.config) == 'undefined') {
52475         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52476         this.cm = this.colModel;
52477         this.cm.xmodule = this.xmodule || false;
52478     }
52479     if (this.dataSource) {
52480         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52481         this.ds = this.dataSource;
52482         this.ds.xmodule = this.xmodule || false;
52483          
52484     }
52485     
52486     
52487     
52488     if(this.width){
52489         this.container.setWidth(this.width);
52490     }
52491
52492     if(this.height){
52493         this.container.setHeight(this.height);
52494     }
52495     /** @private */
52496         this.addEvents({
52497         // raw events
52498         /**
52499          * @event click
52500          * The raw click event for the entire grid.
52501          * @param {Roo.EventObject} e
52502          */
52503         "click" : true,
52504         /**
52505          * @event dblclick
52506          * The raw dblclick event for the entire grid.
52507          * @param {Roo.EventObject} e
52508          */
52509         "dblclick" : true,
52510         /**
52511          * @event contextmenu
52512          * The raw contextmenu event for the entire grid.
52513          * @param {Roo.EventObject} e
52514          */
52515         "contextmenu" : true,
52516         /**
52517          * @event mousedown
52518          * The raw mousedown event for the entire grid.
52519          * @param {Roo.EventObject} e
52520          */
52521         "mousedown" : true,
52522         /**
52523          * @event mouseup
52524          * The raw mouseup event for the entire grid.
52525          * @param {Roo.EventObject} e
52526          */
52527         "mouseup" : true,
52528         /**
52529          * @event mouseover
52530          * The raw mouseover event for the entire grid.
52531          * @param {Roo.EventObject} e
52532          */
52533         "mouseover" : true,
52534         /**
52535          * @event mouseout
52536          * The raw mouseout event for the entire grid.
52537          * @param {Roo.EventObject} e
52538          */
52539         "mouseout" : true,
52540         /**
52541          * @event keypress
52542          * The raw keypress event for the entire grid.
52543          * @param {Roo.EventObject} e
52544          */
52545         "keypress" : true,
52546         /**
52547          * @event keydown
52548          * The raw keydown event for the entire grid.
52549          * @param {Roo.EventObject} e
52550          */
52551         "keydown" : true,
52552
52553         // custom events
52554
52555         /**
52556          * @event cellclick
52557          * Fires when a cell is clicked
52558          * @param {Grid} this
52559          * @param {Number} rowIndex
52560          * @param {Number} columnIndex
52561          * @param {Roo.EventObject} e
52562          */
52563         "cellclick" : true,
52564         /**
52565          * @event celldblclick
52566          * Fires when a cell is double clicked
52567          * @param {Grid} this
52568          * @param {Number} rowIndex
52569          * @param {Number} columnIndex
52570          * @param {Roo.EventObject} e
52571          */
52572         "celldblclick" : true,
52573         /**
52574          * @event rowclick
52575          * Fires when a row is clicked
52576          * @param {Grid} this
52577          * @param {Number} rowIndex
52578          * @param {Roo.EventObject} e
52579          */
52580         "rowclick" : true,
52581         /**
52582          * @event rowdblclick
52583          * Fires when a row is double clicked
52584          * @param {Grid} this
52585          * @param {Number} rowIndex
52586          * @param {Roo.EventObject} e
52587          */
52588         "rowdblclick" : true,
52589         /**
52590          * @event headerclick
52591          * Fires when a header is clicked
52592          * @param {Grid} this
52593          * @param {Number} columnIndex
52594          * @param {Roo.EventObject} e
52595          */
52596         "headerclick" : true,
52597         /**
52598          * @event headerdblclick
52599          * Fires when a header cell is double clicked
52600          * @param {Grid} this
52601          * @param {Number} columnIndex
52602          * @param {Roo.EventObject} e
52603          */
52604         "headerdblclick" : true,
52605         /**
52606          * @event rowcontextmenu
52607          * Fires when a row is right clicked
52608          * @param {Grid} this
52609          * @param {Number} rowIndex
52610          * @param {Roo.EventObject} e
52611          */
52612         "rowcontextmenu" : true,
52613         /**
52614          * @event cellcontextmenu
52615          * Fires when a cell is right clicked
52616          * @param {Grid} this
52617          * @param {Number} rowIndex
52618          * @param {Number} cellIndex
52619          * @param {Roo.EventObject} e
52620          */
52621          "cellcontextmenu" : true,
52622         /**
52623          * @event headercontextmenu
52624          * Fires when a header is right clicked
52625          * @param {Grid} this
52626          * @param {Number} columnIndex
52627          * @param {Roo.EventObject} e
52628          */
52629         "headercontextmenu" : true,
52630         /**
52631          * @event bodyscroll
52632          * Fires when the body element is scrolled
52633          * @param {Number} scrollLeft
52634          * @param {Number} scrollTop
52635          */
52636         "bodyscroll" : true,
52637         /**
52638          * @event columnresize
52639          * Fires when the user resizes a column
52640          * @param {Number} columnIndex
52641          * @param {Number} newSize
52642          */
52643         "columnresize" : true,
52644         /**
52645          * @event columnmove
52646          * Fires when the user moves a column
52647          * @param {Number} oldIndex
52648          * @param {Number} newIndex
52649          */
52650         "columnmove" : true,
52651         /**
52652          * @event startdrag
52653          * Fires when row(s) start being dragged
52654          * @param {Grid} this
52655          * @param {Roo.GridDD} dd The drag drop object
52656          * @param {event} e The raw browser event
52657          */
52658         "startdrag" : true,
52659         /**
52660          * @event enddrag
52661          * Fires when a drag operation is complete
52662          * @param {Grid} this
52663          * @param {Roo.GridDD} dd The drag drop object
52664          * @param {event} e The raw browser event
52665          */
52666         "enddrag" : true,
52667         /**
52668          * @event dragdrop
52669          * Fires when dragged row(s) are dropped on a valid DD target
52670          * @param {Grid} this
52671          * @param {Roo.GridDD} dd The drag drop object
52672          * @param {String} targetId The target drag drop object
52673          * @param {event} e The raw browser event
52674          */
52675         "dragdrop" : true,
52676         /**
52677          * @event dragover
52678          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52679          * @param {Grid} this
52680          * @param {Roo.GridDD} dd The drag drop object
52681          * @param {String} targetId The target drag drop object
52682          * @param {event} e The raw browser event
52683          */
52684         "dragover" : true,
52685         /**
52686          * @event dragenter
52687          *  Fires when the dragged row(s) first cross another DD target while being dragged
52688          * @param {Grid} this
52689          * @param {Roo.GridDD} dd The drag drop object
52690          * @param {String} targetId The target drag drop object
52691          * @param {event} e The raw browser event
52692          */
52693         "dragenter" : true,
52694         /**
52695          * @event dragout
52696          * Fires when the dragged row(s) leave another DD target while being dragged
52697          * @param {Grid} this
52698          * @param {Roo.GridDD} dd The drag drop object
52699          * @param {String} targetId The target drag drop object
52700          * @param {event} e The raw browser event
52701          */
52702         "dragout" : true,
52703         /**
52704          * @event rowclass
52705          * Fires when a row is rendered, so you can change add a style to it.
52706          * @param {GridView} gridview   The grid view
52707          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52708          */
52709         'rowclass' : true,
52710
52711         /**
52712          * @event render
52713          * Fires when the grid is rendered
52714          * @param {Grid} grid
52715          */
52716         'render' : true
52717     });
52718
52719     Roo.grid.Grid.superclass.constructor.call(this);
52720 };
52721 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52722     
52723     /**
52724      * @cfg {String} ddGroup - drag drop group.
52725      */
52726
52727     /**
52728      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52729      */
52730     minColumnWidth : 25,
52731
52732     /**
52733      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52734      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52735      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52736      */
52737     autoSizeColumns : false,
52738
52739     /**
52740      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52741      */
52742     autoSizeHeaders : true,
52743
52744     /**
52745      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52746      */
52747     monitorWindowResize : true,
52748
52749     /**
52750      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52751      * rows measured to get a columns size. Default is 0 (all rows).
52752      */
52753     maxRowsToMeasure : 0,
52754
52755     /**
52756      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52757      */
52758     trackMouseOver : true,
52759
52760     /**
52761     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52762     */
52763     
52764     /**
52765     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52766     */
52767     enableDragDrop : false,
52768     
52769     /**
52770     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52771     */
52772     enableColumnMove : true,
52773     
52774     /**
52775     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52776     */
52777     enableColumnHide : true,
52778     
52779     /**
52780     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52781     */
52782     enableRowHeightSync : false,
52783     
52784     /**
52785     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52786     */
52787     stripeRows : true,
52788     
52789     /**
52790     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52791     */
52792     autoHeight : false,
52793
52794     /**
52795      * @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.
52796      */
52797     autoExpandColumn : false,
52798
52799     /**
52800     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52801     * Default is 50.
52802     */
52803     autoExpandMin : 50,
52804
52805     /**
52806     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52807     */
52808     autoExpandMax : 1000,
52809
52810     /**
52811     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52812     */
52813     view : null,
52814
52815     /**
52816     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52817     */
52818     loadMask : false,
52819     /**
52820     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52821     */
52822     dropTarget: false,
52823     
52824    
52825     
52826     // private
52827     rendered : false,
52828
52829     /**
52830     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52831     * of a fixed width. Default is false.
52832     */
52833     /**
52834     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52835     */
52836     /**
52837      * Called once after all setup has been completed and the grid is ready to be rendered.
52838      * @return {Roo.grid.Grid} this
52839      */
52840     render : function()
52841     {
52842         var c = this.container;
52843         // try to detect autoHeight/width mode
52844         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52845             this.autoHeight = true;
52846         }
52847         var view = this.getView();
52848         view.init(this);
52849
52850         c.on("click", this.onClick, this);
52851         c.on("dblclick", this.onDblClick, this);
52852         c.on("contextmenu", this.onContextMenu, this);
52853         c.on("keydown", this.onKeyDown, this);
52854         if (Roo.isTouch) {
52855             c.on("touchstart", this.onTouchStart, this);
52856         }
52857
52858         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52859
52860         this.getSelectionModel().init(this);
52861
52862         view.render();
52863
52864         if(this.loadMask){
52865             this.loadMask = new Roo.LoadMask(this.container,
52866                     Roo.apply({store:this.dataSource}, this.loadMask));
52867         }
52868         
52869         
52870         if (this.toolbar && this.toolbar.xtype) {
52871             this.toolbar.container = this.getView().getHeaderPanel(true);
52872             this.toolbar = new Roo.Toolbar(this.toolbar);
52873         }
52874         if (this.footer && this.footer.xtype) {
52875             this.footer.dataSource = this.getDataSource();
52876             this.footer.container = this.getView().getFooterPanel(true);
52877             this.footer = Roo.factory(this.footer, Roo);
52878         }
52879         if (this.dropTarget && this.dropTarget.xtype) {
52880             delete this.dropTarget.xtype;
52881             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52882         }
52883         
52884         
52885         this.rendered = true;
52886         this.fireEvent('render', this);
52887         return this;
52888     },
52889
52890         /**
52891          * Reconfigures the grid to use a different Store and Column Model.
52892          * The View will be bound to the new objects and refreshed.
52893          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52894          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52895          */
52896     reconfigure : function(dataSource, colModel){
52897         if(this.loadMask){
52898             this.loadMask.destroy();
52899             this.loadMask = new Roo.LoadMask(this.container,
52900                     Roo.apply({store:dataSource}, this.loadMask));
52901         }
52902         this.view.bind(dataSource, colModel);
52903         this.dataSource = dataSource;
52904         this.colModel = colModel;
52905         this.view.refresh(true);
52906     },
52907
52908     // private
52909     onKeyDown : function(e){
52910         this.fireEvent("keydown", e);
52911     },
52912
52913     /**
52914      * Destroy this grid.
52915      * @param {Boolean} removeEl True to remove the element
52916      */
52917     destroy : function(removeEl, keepListeners){
52918         if(this.loadMask){
52919             this.loadMask.destroy();
52920         }
52921         var c = this.container;
52922         c.removeAllListeners();
52923         this.view.destroy();
52924         this.colModel.purgeListeners();
52925         if(!keepListeners){
52926             this.purgeListeners();
52927         }
52928         c.update("");
52929         if(removeEl === true){
52930             c.remove();
52931         }
52932     },
52933
52934     // private
52935     processEvent : function(name, e){
52936         // does this fire select???
52937         //Roo.log('grid:processEvent '  + name);
52938         
52939         if (name != 'touchstart' ) {
52940             this.fireEvent(name, e);    
52941         }
52942         
52943         var t = e.getTarget();
52944         var v = this.view;
52945         var header = v.findHeaderIndex(t);
52946         if(header !== false){
52947             var ename = name == 'touchstart' ? 'click' : name;
52948              
52949             this.fireEvent("header" + ename, this, header, e);
52950         }else{
52951             var row = v.findRowIndex(t);
52952             var cell = v.findCellIndex(t);
52953             if (name == 'touchstart') {
52954                 // first touch is always a click.
52955                 // hopefull this happens after selection is updated.?
52956                 name = false;
52957                 
52958                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52959                     var cs = this.selModel.getSelectedCell();
52960                     if (row == cs[0] && cell == cs[1]){
52961                         name = 'dblclick';
52962                     }
52963                 }
52964                 if (typeof(this.selModel.getSelections) != 'undefined') {
52965                     var cs = this.selModel.getSelections();
52966                     var ds = this.dataSource;
52967                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52968                         name = 'dblclick';
52969                     }
52970                 }
52971                 if (!name) {
52972                     return;
52973                 }
52974             }
52975             
52976             
52977             if(row !== false){
52978                 this.fireEvent("row" + name, this, row, e);
52979                 if(cell !== false){
52980                     this.fireEvent("cell" + name, this, row, cell, e);
52981                 }
52982             }
52983         }
52984     },
52985
52986     // private
52987     onClick : function(e){
52988         this.processEvent("click", e);
52989     },
52990    // private
52991     onTouchStart : function(e){
52992         this.processEvent("touchstart", e);
52993     },
52994
52995     // private
52996     onContextMenu : function(e, t){
52997         this.processEvent("contextmenu", e);
52998     },
52999
53000     // private
53001     onDblClick : function(e){
53002         this.processEvent("dblclick", e);
53003     },
53004
53005     // private
53006     walkCells : function(row, col, step, fn, scope){
53007         var cm = this.colModel, clen = cm.getColumnCount();
53008         var ds = this.dataSource, rlen = ds.getCount(), first = true;
53009         if(step < 0){
53010             if(col < 0){
53011                 row--;
53012                 first = false;
53013             }
53014             while(row >= 0){
53015                 if(!first){
53016                     col = clen-1;
53017                 }
53018                 first = false;
53019                 while(col >= 0){
53020                     if(fn.call(scope || this, row, col, cm) === true){
53021                         return [row, col];
53022                     }
53023                     col--;
53024                 }
53025                 row--;
53026             }
53027         } else {
53028             if(col >= clen){
53029                 row++;
53030                 first = false;
53031             }
53032             while(row < rlen){
53033                 if(!first){
53034                     col = 0;
53035                 }
53036                 first = false;
53037                 while(col < clen){
53038                     if(fn.call(scope || this, row, col, cm) === true){
53039                         return [row, col];
53040                     }
53041                     col++;
53042                 }
53043                 row++;
53044             }
53045         }
53046         return null;
53047     },
53048
53049     // private
53050     getSelections : function(){
53051         return this.selModel.getSelections();
53052     },
53053
53054     /**
53055      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
53056      * but if manual update is required this method will initiate it.
53057      */
53058     autoSize : function(){
53059         if(this.rendered){
53060             this.view.layout();
53061             if(this.view.adjustForScroll){
53062                 this.view.adjustForScroll();
53063             }
53064         }
53065     },
53066
53067     /**
53068      * Returns the grid's underlying element.
53069      * @return {Element} The element
53070      */
53071     getGridEl : function(){
53072         return this.container;
53073     },
53074
53075     // private for compatibility, overridden by editor grid
53076     stopEditing : function(){},
53077
53078     /**
53079      * Returns the grid's SelectionModel.
53080      * @return {SelectionModel}
53081      */
53082     getSelectionModel : function(){
53083         if(!this.selModel){
53084             this.selModel = new Roo.grid.RowSelectionModel();
53085         }
53086         return this.selModel;
53087     },
53088
53089     /**
53090      * Returns the grid's DataSource.
53091      * @return {DataSource}
53092      */
53093     getDataSource : function(){
53094         return this.dataSource;
53095     },
53096
53097     /**
53098      * Returns the grid's ColumnModel.
53099      * @return {ColumnModel}
53100      */
53101     getColumnModel : function(){
53102         return this.colModel;
53103     },
53104
53105     /**
53106      * Returns the grid's GridView object.
53107      * @return {GridView}
53108      */
53109     getView : function(){
53110         if(!this.view){
53111             this.view = new Roo.grid.GridView(this.viewConfig);
53112         }
53113         return this.view;
53114     },
53115     /**
53116      * Called to get grid's drag proxy text, by default returns this.ddText.
53117      * @return {String}
53118      */
53119     getDragDropText : function(){
53120         var count = this.selModel.getCount();
53121         return String.format(this.ddText, count, count == 1 ? '' : 's');
53122     }
53123 });
53124 /**
53125  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53126  * %0 is replaced with the number of selected rows.
53127  * @type String
53128  */
53129 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53130  * Based on:
53131  * Ext JS Library 1.1.1
53132  * Copyright(c) 2006-2007, Ext JS, LLC.
53133  *
53134  * Originally Released Under LGPL - original licence link has changed is not relivant.
53135  *
53136  * Fork - LGPL
53137  * <script type="text/javascript">
53138  */
53139  
53140 Roo.grid.AbstractGridView = function(){
53141         this.grid = null;
53142         
53143         this.events = {
53144             "beforerowremoved" : true,
53145             "beforerowsinserted" : true,
53146             "beforerefresh" : true,
53147             "rowremoved" : true,
53148             "rowsinserted" : true,
53149             "rowupdated" : true,
53150             "refresh" : true
53151         };
53152     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53153 };
53154
53155 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53156     rowClass : "x-grid-row",
53157     cellClass : "x-grid-cell",
53158     tdClass : "x-grid-td",
53159     hdClass : "x-grid-hd",
53160     splitClass : "x-grid-hd-split",
53161     
53162     init: function(grid){
53163         this.grid = grid;
53164                 var cid = this.grid.getGridEl().id;
53165         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53166         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53167         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53168         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53169         },
53170         
53171     getColumnRenderers : function(){
53172         var renderers = [];
53173         var cm = this.grid.colModel;
53174         var colCount = cm.getColumnCount();
53175         for(var i = 0; i < colCount; i++){
53176             renderers[i] = cm.getRenderer(i);
53177         }
53178         return renderers;
53179     },
53180     
53181     getColumnIds : function(){
53182         var ids = [];
53183         var cm = this.grid.colModel;
53184         var colCount = cm.getColumnCount();
53185         for(var i = 0; i < colCount; i++){
53186             ids[i] = cm.getColumnId(i);
53187         }
53188         return ids;
53189     },
53190     
53191     getDataIndexes : function(){
53192         if(!this.indexMap){
53193             this.indexMap = this.buildIndexMap();
53194         }
53195         return this.indexMap.colToData;
53196     },
53197     
53198     getColumnIndexByDataIndex : function(dataIndex){
53199         if(!this.indexMap){
53200             this.indexMap = this.buildIndexMap();
53201         }
53202         return this.indexMap.dataToCol[dataIndex];
53203     },
53204     
53205     /**
53206      * Set a css style for a column dynamically. 
53207      * @param {Number} colIndex The index of the column
53208      * @param {String} name The css property name
53209      * @param {String} value The css value
53210      */
53211     setCSSStyle : function(colIndex, name, value){
53212         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53213         Roo.util.CSS.updateRule(selector, name, value);
53214     },
53215     
53216     generateRules : function(cm){
53217         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53218         Roo.util.CSS.removeStyleSheet(rulesId);
53219         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53220             var cid = cm.getColumnId(i);
53221             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53222                          this.tdSelector, cid, " {\n}\n",
53223                          this.hdSelector, cid, " {\n}\n",
53224                          this.splitSelector, cid, " {\n}\n");
53225         }
53226         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53227     }
53228 });/*
53229  * Based on:
53230  * Ext JS Library 1.1.1
53231  * Copyright(c) 2006-2007, Ext JS, LLC.
53232  *
53233  * Originally Released Under LGPL - original licence link has changed is not relivant.
53234  *
53235  * Fork - LGPL
53236  * <script type="text/javascript">
53237  */
53238
53239 // private
53240 // This is a support class used internally by the Grid components
53241 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53242     this.grid = grid;
53243     this.view = grid.getView();
53244     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53245     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53246     if(hd2){
53247         this.setHandleElId(Roo.id(hd));
53248         this.setOuterHandleElId(Roo.id(hd2));
53249     }
53250     this.scroll = false;
53251 };
53252 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53253     maxDragWidth: 120,
53254     getDragData : function(e){
53255         var t = Roo.lib.Event.getTarget(e);
53256         var h = this.view.findHeaderCell(t);
53257         if(h){
53258             return {ddel: h.firstChild, header:h};
53259         }
53260         return false;
53261     },
53262
53263     onInitDrag : function(e){
53264         this.view.headersDisabled = true;
53265         var clone = this.dragData.ddel.cloneNode(true);
53266         clone.id = Roo.id();
53267         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53268         this.proxy.update(clone);
53269         return true;
53270     },
53271
53272     afterValidDrop : function(){
53273         var v = this.view;
53274         setTimeout(function(){
53275             v.headersDisabled = false;
53276         }, 50);
53277     },
53278
53279     afterInvalidDrop : function(){
53280         var v = this.view;
53281         setTimeout(function(){
53282             v.headersDisabled = false;
53283         }, 50);
53284     }
53285 });
53286 /*
53287  * Based on:
53288  * Ext JS Library 1.1.1
53289  * Copyright(c) 2006-2007, Ext JS, LLC.
53290  *
53291  * Originally Released Under LGPL - original licence link has changed is not relivant.
53292  *
53293  * Fork - LGPL
53294  * <script type="text/javascript">
53295  */
53296 // private
53297 // This is a support class used internally by the Grid components
53298 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53299     this.grid = grid;
53300     this.view = grid.getView();
53301     // split the proxies so they don't interfere with mouse events
53302     this.proxyTop = Roo.DomHelper.append(document.body, {
53303         cls:"col-move-top", html:"&#160;"
53304     }, true);
53305     this.proxyBottom = Roo.DomHelper.append(document.body, {
53306         cls:"col-move-bottom", html:"&#160;"
53307     }, true);
53308     this.proxyTop.hide = this.proxyBottom.hide = function(){
53309         this.setLeftTop(-100,-100);
53310         this.setStyle("visibility", "hidden");
53311     };
53312     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53313     // temporarily disabled
53314     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53315     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53316 };
53317 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53318     proxyOffsets : [-4, -9],
53319     fly: Roo.Element.fly,
53320
53321     getTargetFromEvent : function(e){
53322         var t = Roo.lib.Event.getTarget(e);
53323         var cindex = this.view.findCellIndex(t);
53324         if(cindex !== false){
53325             return this.view.getHeaderCell(cindex);
53326         }
53327         return null;
53328     },
53329
53330     nextVisible : function(h){
53331         var v = this.view, cm = this.grid.colModel;
53332         h = h.nextSibling;
53333         while(h){
53334             if(!cm.isHidden(v.getCellIndex(h))){
53335                 return h;
53336             }
53337             h = h.nextSibling;
53338         }
53339         return null;
53340     },
53341
53342     prevVisible : function(h){
53343         var v = this.view, cm = this.grid.colModel;
53344         h = h.prevSibling;
53345         while(h){
53346             if(!cm.isHidden(v.getCellIndex(h))){
53347                 return h;
53348             }
53349             h = h.prevSibling;
53350         }
53351         return null;
53352     },
53353
53354     positionIndicator : function(h, n, e){
53355         var x = Roo.lib.Event.getPageX(e);
53356         var r = Roo.lib.Dom.getRegion(n.firstChild);
53357         var px, pt, py = r.top + this.proxyOffsets[1];
53358         if((r.right - x) <= (r.right-r.left)/2){
53359             px = r.right+this.view.borderWidth;
53360             pt = "after";
53361         }else{
53362             px = r.left;
53363             pt = "before";
53364         }
53365         var oldIndex = this.view.getCellIndex(h);
53366         var newIndex = this.view.getCellIndex(n);
53367
53368         if(this.grid.colModel.isFixed(newIndex)){
53369             return false;
53370         }
53371
53372         var locked = this.grid.colModel.isLocked(newIndex);
53373
53374         if(pt == "after"){
53375             newIndex++;
53376         }
53377         if(oldIndex < newIndex){
53378             newIndex--;
53379         }
53380         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53381             return false;
53382         }
53383         px +=  this.proxyOffsets[0];
53384         this.proxyTop.setLeftTop(px, py);
53385         this.proxyTop.show();
53386         if(!this.bottomOffset){
53387             this.bottomOffset = this.view.mainHd.getHeight();
53388         }
53389         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53390         this.proxyBottom.show();
53391         return pt;
53392     },
53393
53394     onNodeEnter : function(n, dd, e, data){
53395         if(data.header != n){
53396             this.positionIndicator(data.header, n, e);
53397         }
53398     },
53399
53400     onNodeOver : function(n, dd, e, data){
53401         var result = false;
53402         if(data.header != n){
53403             result = this.positionIndicator(data.header, n, e);
53404         }
53405         if(!result){
53406             this.proxyTop.hide();
53407             this.proxyBottom.hide();
53408         }
53409         return result ? this.dropAllowed : this.dropNotAllowed;
53410     },
53411
53412     onNodeOut : function(n, dd, e, data){
53413         this.proxyTop.hide();
53414         this.proxyBottom.hide();
53415     },
53416
53417     onNodeDrop : function(n, dd, e, data){
53418         var h = data.header;
53419         if(h != n){
53420             var cm = this.grid.colModel;
53421             var x = Roo.lib.Event.getPageX(e);
53422             var r = Roo.lib.Dom.getRegion(n.firstChild);
53423             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53424             var oldIndex = this.view.getCellIndex(h);
53425             var newIndex = this.view.getCellIndex(n);
53426             var locked = cm.isLocked(newIndex);
53427             if(pt == "after"){
53428                 newIndex++;
53429             }
53430             if(oldIndex < newIndex){
53431                 newIndex--;
53432             }
53433             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53434                 return false;
53435             }
53436             cm.setLocked(oldIndex, locked, true);
53437             cm.moveColumn(oldIndex, newIndex);
53438             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53439             return true;
53440         }
53441         return false;
53442     }
53443 });
53444 /*
53445  * Based on:
53446  * Ext JS Library 1.1.1
53447  * Copyright(c) 2006-2007, Ext JS, LLC.
53448  *
53449  * Originally Released Under LGPL - original licence link has changed is not relivant.
53450  *
53451  * Fork - LGPL
53452  * <script type="text/javascript">
53453  */
53454   
53455 /**
53456  * @class Roo.grid.GridView
53457  * @extends Roo.util.Observable
53458  *
53459  * @constructor
53460  * @param {Object} config
53461  */
53462 Roo.grid.GridView = function(config){
53463     Roo.grid.GridView.superclass.constructor.call(this);
53464     this.el = null;
53465
53466     Roo.apply(this, config);
53467 };
53468
53469 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53470
53471     unselectable :  'unselectable="on"',
53472     unselectableCls :  'x-unselectable',
53473     
53474     
53475     rowClass : "x-grid-row",
53476
53477     cellClass : "x-grid-col",
53478
53479     tdClass : "x-grid-td",
53480
53481     hdClass : "x-grid-hd",
53482
53483     splitClass : "x-grid-split",
53484
53485     sortClasses : ["sort-asc", "sort-desc"],
53486
53487     enableMoveAnim : false,
53488
53489     hlColor: "C3DAF9",
53490
53491     dh : Roo.DomHelper,
53492
53493     fly : Roo.Element.fly,
53494
53495     css : Roo.util.CSS,
53496
53497     borderWidth: 1,
53498
53499     splitOffset: 3,
53500
53501     scrollIncrement : 22,
53502
53503     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53504
53505     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53506
53507     bind : function(ds, cm){
53508         if(this.ds){
53509             this.ds.un("load", this.onLoad, this);
53510             this.ds.un("datachanged", this.onDataChange, this);
53511             this.ds.un("add", this.onAdd, this);
53512             this.ds.un("remove", this.onRemove, this);
53513             this.ds.un("update", this.onUpdate, this);
53514             this.ds.un("clear", this.onClear, this);
53515         }
53516         if(ds){
53517             ds.on("load", this.onLoad, this);
53518             ds.on("datachanged", this.onDataChange, this);
53519             ds.on("add", this.onAdd, this);
53520             ds.on("remove", this.onRemove, this);
53521             ds.on("update", this.onUpdate, this);
53522             ds.on("clear", this.onClear, this);
53523         }
53524         this.ds = ds;
53525
53526         if(this.cm){
53527             this.cm.un("widthchange", this.onColWidthChange, this);
53528             this.cm.un("headerchange", this.onHeaderChange, this);
53529             this.cm.un("hiddenchange", this.onHiddenChange, this);
53530             this.cm.un("columnmoved", this.onColumnMove, this);
53531             this.cm.un("columnlockchange", this.onColumnLock, this);
53532         }
53533         if(cm){
53534             this.generateRules(cm);
53535             cm.on("widthchange", this.onColWidthChange, this);
53536             cm.on("headerchange", this.onHeaderChange, this);
53537             cm.on("hiddenchange", this.onHiddenChange, this);
53538             cm.on("columnmoved", this.onColumnMove, this);
53539             cm.on("columnlockchange", this.onColumnLock, this);
53540         }
53541         this.cm = cm;
53542     },
53543
53544     init: function(grid){
53545         Roo.grid.GridView.superclass.init.call(this, grid);
53546
53547         this.bind(grid.dataSource, grid.colModel);
53548
53549         grid.on("headerclick", this.handleHeaderClick, this);
53550
53551         if(grid.trackMouseOver){
53552             grid.on("mouseover", this.onRowOver, this);
53553             grid.on("mouseout", this.onRowOut, this);
53554         }
53555         grid.cancelTextSelection = function(){};
53556         this.gridId = grid.id;
53557
53558         var tpls = this.templates || {};
53559
53560         if(!tpls.master){
53561             tpls.master = new Roo.Template(
53562                '<div class="x-grid" hidefocus="true">',
53563                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53564                   '<div class="x-grid-topbar"></div>',
53565                   '<div class="x-grid-scroller"><div></div></div>',
53566                   '<div class="x-grid-locked">',
53567                       '<div class="x-grid-header">{lockedHeader}</div>',
53568                       '<div class="x-grid-body">{lockedBody}</div>',
53569                   "</div>",
53570                   '<div class="x-grid-viewport">',
53571                       '<div class="x-grid-header">{header}</div>',
53572                       '<div class="x-grid-body">{body}</div>',
53573                   "</div>",
53574                   '<div class="x-grid-bottombar"></div>',
53575                  
53576                   '<div class="x-grid-resize-proxy">&#160;</div>',
53577                "</div>"
53578             );
53579             tpls.master.disableformats = true;
53580         }
53581
53582         if(!tpls.header){
53583             tpls.header = new Roo.Template(
53584                '<table border="0" cellspacing="0" cellpadding="0">',
53585                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53586                "</table>{splits}"
53587             );
53588             tpls.header.disableformats = true;
53589         }
53590         tpls.header.compile();
53591
53592         if(!tpls.hcell){
53593             tpls.hcell = new Roo.Template(
53594                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53595                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53596                 "</div></td>"
53597              );
53598              tpls.hcell.disableFormats = true;
53599         }
53600         tpls.hcell.compile();
53601
53602         if(!tpls.hsplit){
53603             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53604                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53605             tpls.hsplit.disableFormats = true;
53606         }
53607         tpls.hsplit.compile();
53608
53609         if(!tpls.body){
53610             tpls.body = new Roo.Template(
53611                '<table border="0" cellspacing="0" cellpadding="0">',
53612                "<tbody>{rows}</tbody>",
53613                "</table>"
53614             );
53615             tpls.body.disableFormats = true;
53616         }
53617         tpls.body.compile();
53618
53619         if(!tpls.row){
53620             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53621             tpls.row.disableFormats = true;
53622         }
53623         tpls.row.compile();
53624
53625         if(!tpls.cell){
53626             tpls.cell = new Roo.Template(
53627                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53628                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53629                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53630                 "</td>"
53631             );
53632             tpls.cell.disableFormats = true;
53633         }
53634         tpls.cell.compile();
53635
53636         this.templates = tpls;
53637     },
53638
53639     // remap these for backwards compat
53640     onColWidthChange : function(){
53641         this.updateColumns.apply(this, arguments);
53642     },
53643     onHeaderChange : function(){
53644         this.updateHeaders.apply(this, arguments);
53645     }, 
53646     onHiddenChange : function(){
53647         this.handleHiddenChange.apply(this, arguments);
53648     },
53649     onColumnMove : function(){
53650         this.handleColumnMove.apply(this, arguments);
53651     },
53652     onColumnLock : function(){
53653         this.handleLockChange.apply(this, arguments);
53654     },
53655
53656     onDataChange : function(){
53657         this.refresh();
53658         this.updateHeaderSortState();
53659     },
53660
53661     onClear : function(){
53662         this.refresh();
53663     },
53664
53665     onUpdate : function(ds, record){
53666         this.refreshRow(record);
53667     },
53668
53669     refreshRow : function(record){
53670         var ds = this.ds, index;
53671         if(typeof record == 'number'){
53672             index = record;
53673             record = ds.getAt(index);
53674         }else{
53675             index = ds.indexOf(record);
53676         }
53677         this.insertRows(ds, index, index, true);
53678         this.onRemove(ds, record, index+1, true);
53679         this.syncRowHeights(index, index);
53680         this.layout();
53681         this.fireEvent("rowupdated", this, index, record);
53682     },
53683
53684     onAdd : function(ds, records, index){
53685         this.insertRows(ds, index, index + (records.length-1));
53686     },
53687
53688     onRemove : function(ds, record, index, isUpdate){
53689         if(isUpdate !== true){
53690             this.fireEvent("beforerowremoved", this, index, record);
53691         }
53692         var bt = this.getBodyTable(), lt = this.getLockedTable();
53693         if(bt.rows[index]){
53694             bt.firstChild.removeChild(bt.rows[index]);
53695         }
53696         if(lt.rows[index]){
53697             lt.firstChild.removeChild(lt.rows[index]);
53698         }
53699         if(isUpdate !== true){
53700             this.stripeRows(index);
53701             this.syncRowHeights(index, index);
53702             this.layout();
53703             this.fireEvent("rowremoved", this, index, record);
53704         }
53705     },
53706
53707     onLoad : function(){
53708         this.scrollToTop();
53709     },
53710
53711     /**
53712      * Scrolls the grid to the top
53713      */
53714     scrollToTop : function(){
53715         if(this.scroller){
53716             this.scroller.dom.scrollTop = 0;
53717             this.syncScroll();
53718         }
53719     },
53720
53721     /**
53722      * Gets a panel in the header of the grid that can be used for toolbars etc.
53723      * After modifying the contents of this panel a call to grid.autoSize() may be
53724      * required to register any changes in size.
53725      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53726      * @return Roo.Element
53727      */
53728     getHeaderPanel : function(doShow){
53729         if(doShow){
53730             this.headerPanel.show();
53731         }
53732         return this.headerPanel;
53733     },
53734
53735     /**
53736      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53737      * After modifying the contents of this panel a call to grid.autoSize() may be
53738      * required to register any changes in size.
53739      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53740      * @return Roo.Element
53741      */
53742     getFooterPanel : function(doShow){
53743         if(doShow){
53744             this.footerPanel.show();
53745         }
53746         return this.footerPanel;
53747     },
53748
53749     initElements : function(){
53750         var E = Roo.Element;
53751         var el = this.grid.getGridEl().dom.firstChild;
53752         var cs = el.childNodes;
53753
53754         this.el = new E(el);
53755         
53756          this.focusEl = new E(el.firstChild);
53757         this.focusEl.swallowEvent("click", true);
53758         
53759         this.headerPanel = new E(cs[1]);
53760         this.headerPanel.enableDisplayMode("block");
53761
53762         this.scroller = new E(cs[2]);
53763         this.scrollSizer = new E(this.scroller.dom.firstChild);
53764
53765         this.lockedWrap = new E(cs[3]);
53766         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53767         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53768
53769         this.mainWrap = new E(cs[4]);
53770         this.mainHd = new E(this.mainWrap.dom.firstChild);
53771         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53772
53773         this.footerPanel = new E(cs[5]);
53774         this.footerPanel.enableDisplayMode("block");
53775
53776         this.resizeProxy = new E(cs[6]);
53777
53778         this.headerSelector = String.format(
53779            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53780            this.lockedHd.id, this.mainHd.id
53781         );
53782
53783         this.splitterSelector = String.format(
53784            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53785            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53786         );
53787     },
53788     idToCssName : function(s)
53789     {
53790         return s.replace(/[^a-z0-9]+/ig, '-');
53791     },
53792
53793     getHeaderCell : function(index){
53794         return Roo.DomQuery.select(this.headerSelector)[index];
53795     },
53796
53797     getHeaderCellMeasure : function(index){
53798         return this.getHeaderCell(index).firstChild;
53799     },
53800
53801     getHeaderCellText : function(index){
53802         return this.getHeaderCell(index).firstChild.firstChild;
53803     },
53804
53805     getLockedTable : function(){
53806         return this.lockedBody.dom.firstChild;
53807     },
53808
53809     getBodyTable : function(){
53810         return this.mainBody.dom.firstChild;
53811     },
53812
53813     getLockedRow : function(index){
53814         return this.getLockedTable().rows[index];
53815     },
53816
53817     getRow : function(index){
53818         return this.getBodyTable().rows[index];
53819     },
53820
53821     getRowComposite : function(index){
53822         if(!this.rowEl){
53823             this.rowEl = new Roo.CompositeElementLite();
53824         }
53825         var els = [], lrow, mrow;
53826         if(lrow = this.getLockedRow(index)){
53827             els.push(lrow);
53828         }
53829         if(mrow = this.getRow(index)){
53830             els.push(mrow);
53831         }
53832         this.rowEl.elements = els;
53833         return this.rowEl;
53834     },
53835     /**
53836      * Gets the 'td' of the cell
53837      * 
53838      * @param {Integer} rowIndex row to select
53839      * @param {Integer} colIndex column to select
53840      * 
53841      * @return {Object} 
53842      */
53843     getCell : function(rowIndex, colIndex){
53844         var locked = this.cm.getLockedCount();
53845         var source;
53846         if(colIndex < locked){
53847             source = this.lockedBody.dom.firstChild;
53848         }else{
53849             source = this.mainBody.dom.firstChild;
53850             colIndex -= locked;
53851         }
53852         return source.rows[rowIndex].childNodes[colIndex];
53853     },
53854
53855     getCellText : function(rowIndex, colIndex){
53856         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53857     },
53858
53859     getCellBox : function(cell){
53860         var b = this.fly(cell).getBox();
53861         if(Roo.isOpera){ // opera fails to report the Y
53862             b.y = cell.offsetTop + this.mainBody.getY();
53863         }
53864         return b;
53865     },
53866
53867     getCellIndex : function(cell){
53868         var id = String(cell.className).match(this.cellRE);
53869         if(id){
53870             return parseInt(id[1], 10);
53871         }
53872         return 0;
53873     },
53874
53875     findHeaderIndex : function(n){
53876         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53877         return r ? this.getCellIndex(r) : false;
53878     },
53879
53880     findHeaderCell : function(n){
53881         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53882         return r ? r : false;
53883     },
53884
53885     findRowIndex : function(n){
53886         if(!n){
53887             return false;
53888         }
53889         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53890         return r ? r.rowIndex : false;
53891     },
53892
53893     findCellIndex : function(node){
53894         var stop = this.el.dom;
53895         while(node && node != stop){
53896             if(this.findRE.test(node.className)){
53897                 return this.getCellIndex(node);
53898             }
53899             node = node.parentNode;
53900         }
53901         return false;
53902     },
53903
53904     getColumnId : function(index){
53905         return this.cm.getColumnId(index);
53906     },
53907
53908     getSplitters : function()
53909     {
53910         if(this.splitterSelector){
53911            return Roo.DomQuery.select(this.splitterSelector);
53912         }else{
53913             return null;
53914       }
53915     },
53916
53917     getSplitter : function(index){
53918         return this.getSplitters()[index];
53919     },
53920
53921     onRowOver : function(e, t){
53922         var row;
53923         if((row = this.findRowIndex(t)) !== false){
53924             this.getRowComposite(row).addClass("x-grid-row-over");
53925         }
53926     },
53927
53928     onRowOut : function(e, t){
53929         var row;
53930         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53931             this.getRowComposite(row).removeClass("x-grid-row-over");
53932         }
53933     },
53934
53935     renderHeaders : function(){
53936         var cm = this.cm;
53937         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53938         var cb = [], lb = [], sb = [], lsb = [], p = {};
53939         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53940             p.cellId = "x-grid-hd-0-" + i;
53941             p.splitId = "x-grid-csplit-0-" + i;
53942             p.id = cm.getColumnId(i);
53943             p.title = cm.getColumnTooltip(i) || "";
53944             p.value = cm.getColumnHeader(i) || "";
53945             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53946             if(!cm.isLocked(i)){
53947                 cb[cb.length] = ct.apply(p);
53948                 sb[sb.length] = st.apply(p);
53949             }else{
53950                 lb[lb.length] = ct.apply(p);
53951                 lsb[lsb.length] = st.apply(p);
53952             }
53953         }
53954         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53955                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53956     },
53957
53958     updateHeaders : function(){
53959         var html = this.renderHeaders();
53960         this.lockedHd.update(html[0]);
53961         this.mainHd.update(html[1]);
53962     },
53963
53964     /**
53965      * Focuses the specified row.
53966      * @param {Number} row The row index
53967      */
53968     focusRow : function(row)
53969     {
53970         //Roo.log('GridView.focusRow');
53971         var x = this.scroller.dom.scrollLeft;
53972         this.focusCell(row, 0, false);
53973         this.scroller.dom.scrollLeft = x;
53974     },
53975
53976     /**
53977      * Focuses the specified cell.
53978      * @param {Number} row The row index
53979      * @param {Number} col The column index
53980      * @param {Boolean} hscroll false to disable horizontal scrolling
53981      */
53982     focusCell : function(row, col, hscroll)
53983     {
53984         //Roo.log('GridView.focusCell');
53985         var el = this.ensureVisible(row, col, hscroll);
53986         this.focusEl.alignTo(el, "tl-tl");
53987         if(Roo.isGecko){
53988             this.focusEl.focus();
53989         }else{
53990             this.focusEl.focus.defer(1, this.focusEl);
53991         }
53992     },
53993
53994     /**
53995      * Scrolls the specified cell into view
53996      * @param {Number} row The row index
53997      * @param {Number} col The column index
53998      * @param {Boolean} hscroll false to disable horizontal scrolling
53999      */
54000     ensureVisible : function(row, col, hscroll)
54001     {
54002         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
54003         //return null; //disable for testing.
54004         if(typeof row != "number"){
54005             row = row.rowIndex;
54006         }
54007         if(row < 0 && row >= this.ds.getCount()){
54008             return  null;
54009         }
54010         col = (col !== undefined ? col : 0);
54011         var cm = this.grid.colModel;
54012         while(cm.isHidden(col)){
54013             col++;
54014         }
54015
54016         var el = this.getCell(row, col);
54017         if(!el){
54018             return null;
54019         }
54020         var c = this.scroller.dom;
54021
54022         var ctop = parseInt(el.offsetTop, 10);
54023         var cleft = parseInt(el.offsetLeft, 10);
54024         var cbot = ctop + el.offsetHeight;
54025         var cright = cleft + el.offsetWidth;
54026         
54027         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
54028         var stop = parseInt(c.scrollTop, 10);
54029         var sleft = parseInt(c.scrollLeft, 10);
54030         var sbot = stop + ch;
54031         var sright = sleft + c.clientWidth;
54032         /*
54033         Roo.log('GridView.ensureVisible:' +
54034                 ' ctop:' + ctop +
54035                 ' c.clientHeight:' + c.clientHeight +
54036                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
54037                 ' stop:' + stop +
54038                 ' cbot:' + cbot +
54039                 ' sbot:' + sbot +
54040                 ' ch:' + ch  
54041                 );
54042         */
54043         if(ctop < stop){
54044              c.scrollTop = ctop;
54045             //Roo.log("set scrolltop to ctop DISABLE?");
54046         }else if(cbot > sbot){
54047             //Roo.log("set scrolltop to cbot-ch");
54048             c.scrollTop = cbot-ch;
54049         }
54050         
54051         if(hscroll !== false){
54052             if(cleft < sleft){
54053                 c.scrollLeft = cleft;
54054             }else if(cright > sright){
54055                 c.scrollLeft = cright-c.clientWidth;
54056             }
54057         }
54058          
54059         return el;
54060     },
54061
54062     updateColumns : function(){
54063         this.grid.stopEditing();
54064         var cm = this.grid.colModel, colIds = this.getColumnIds();
54065         //var totalWidth = cm.getTotalWidth();
54066         var pos = 0;
54067         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54068             //if(cm.isHidden(i)) continue;
54069             var w = cm.getColumnWidth(i);
54070             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
54071             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
54072         }
54073         this.updateSplitters();
54074     },
54075
54076     generateRules : function(cm){
54077         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
54078         Roo.util.CSS.removeStyleSheet(rulesId);
54079         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54080             var cid = cm.getColumnId(i);
54081             var align = '';
54082             if(cm.config[i].align){
54083                 align = 'text-align:'+cm.config[i].align+';';
54084             }
54085             var hidden = '';
54086             if(cm.isHidden(i)){
54087                 hidden = 'display:none;';
54088             }
54089             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
54090             ruleBuf.push(
54091                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
54092                     this.hdSelector, cid, " {\n", align, width, "}\n",
54093                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
54094                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
54095         }
54096         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54097     },
54098
54099     updateSplitters : function(){
54100         var cm = this.cm, s = this.getSplitters();
54101         if(s){ // splitters not created yet
54102             var pos = 0, locked = true;
54103             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54104                 if(cm.isHidden(i)) {
54105                     continue;
54106                 }
54107                 var w = cm.getColumnWidth(i); // make sure it's a number
54108                 if(!cm.isLocked(i) && locked){
54109                     pos = 0;
54110                     locked = false;
54111                 }
54112                 pos += w;
54113                 s[i].style.left = (pos-this.splitOffset) + "px";
54114             }
54115         }
54116     },
54117
54118     handleHiddenChange : function(colModel, colIndex, hidden){
54119         if(hidden){
54120             this.hideColumn(colIndex);
54121         }else{
54122             this.unhideColumn(colIndex);
54123         }
54124     },
54125
54126     hideColumn : function(colIndex){
54127         var cid = this.getColumnId(colIndex);
54128         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54129         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54130         if(Roo.isSafari){
54131             this.updateHeaders();
54132         }
54133         this.updateSplitters();
54134         this.layout();
54135     },
54136
54137     unhideColumn : function(colIndex){
54138         var cid = this.getColumnId(colIndex);
54139         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54140         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54141
54142         if(Roo.isSafari){
54143             this.updateHeaders();
54144         }
54145         this.updateSplitters();
54146         this.layout();
54147     },
54148
54149     insertRows : function(dm, firstRow, lastRow, isUpdate){
54150         if(firstRow == 0 && lastRow == dm.getCount()-1){
54151             this.refresh();
54152         }else{
54153             if(!isUpdate){
54154                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54155             }
54156             var s = this.getScrollState();
54157             var markup = this.renderRows(firstRow, lastRow);
54158             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54159             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54160             this.restoreScroll(s);
54161             if(!isUpdate){
54162                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54163                 this.syncRowHeights(firstRow, lastRow);
54164                 this.stripeRows(firstRow);
54165                 this.layout();
54166             }
54167         }
54168     },
54169
54170     bufferRows : function(markup, target, index){
54171         var before = null, trows = target.rows, tbody = target.tBodies[0];
54172         if(index < trows.length){
54173             before = trows[index];
54174         }
54175         var b = document.createElement("div");
54176         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54177         var rows = b.firstChild.rows;
54178         for(var i = 0, len = rows.length; i < len; i++){
54179             if(before){
54180                 tbody.insertBefore(rows[0], before);
54181             }else{
54182                 tbody.appendChild(rows[0]);
54183             }
54184         }
54185         b.innerHTML = "";
54186         b = null;
54187     },
54188
54189     deleteRows : function(dm, firstRow, lastRow){
54190         if(dm.getRowCount()<1){
54191             this.fireEvent("beforerefresh", this);
54192             this.mainBody.update("");
54193             this.lockedBody.update("");
54194             this.fireEvent("refresh", this);
54195         }else{
54196             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54197             var bt = this.getBodyTable();
54198             var tbody = bt.firstChild;
54199             var rows = bt.rows;
54200             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54201                 tbody.removeChild(rows[firstRow]);
54202             }
54203             this.stripeRows(firstRow);
54204             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54205         }
54206     },
54207
54208     updateRows : function(dataSource, firstRow, lastRow){
54209         var s = this.getScrollState();
54210         this.refresh();
54211         this.restoreScroll(s);
54212     },
54213
54214     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54215         if(!noRefresh){
54216            this.refresh();
54217         }
54218         this.updateHeaderSortState();
54219     },
54220
54221     getScrollState : function(){
54222         
54223         var sb = this.scroller.dom;
54224         return {left: sb.scrollLeft, top: sb.scrollTop};
54225     },
54226
54227     stripeRows : function(startRow){
54228         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54229             return;
54230         }
54231         startRow = startRow || 0;
54232         var rows = this.getBodyTable().rows;
54233         var lrows = this.getLockedTable().rows;
54234         var cls = ' x-grid-row-alt ';
54235         for(var i = startRow, len = rows.length; i < len; i++){
54236             var row = rows[i], lrow = lrows[i];
54237             var isAlt = ((i+1) % 2 == 0);
54238             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54239             if(isAlt == hasAlt){
54240                 continue;
54241             }
54242             if(isAlt){
54243                 row.className += " x-grid-row-alt";
54244             }else{
54245                 row.className = row.className.replace("x-grid-row-alt", "");
54246             }
54247             if(lrow){
54248                 lrow.className = row.className;
54249             }
54250         }
54251     },
54252
54253     restoreScroll : function(state){
54254         //Roo.log('GridView.restoreScroll');
54255         var sb = this.scroller.dom;
54256         sb.scrollLeft = state.left;
54257         sb.scrollTop = state.top;
54258         this.syncScroll();
54259     },
54260
54261     syncScroll : function(){
54262         //Roo.log('GridView.syncScroll');
54263         var sb = this.scroller.dom;
54264         var sh = this.mainHd.dom;
54265         var bs = this.mainBody.dom;
54266         var lv = this.lockedBody.dom;
54267         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54268         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54269     },
54270
54271     handleScroll : function(e){
54272         this.syncScroll();
54273         var sb = this.scroller.dom;
54274         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54275         e.stopEvent();
54276     },
54277
54278     handleWheel : function(e){
54279         var d = e.getWheelDelta();
54280         this.scroller.dom.scrollTop -= d*22;
54281         // set this here to prevent jumpy scrolling on large tables
54282         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54283         e.stopEvent();
54284     },
54285
54286     renderRows : function(startRow, endRow){
54287         // pull in all the crap needed to render rows
54288         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54289         var colCount = cm.getColumnCount();
54290
54291         if(ds.getCount() < 1){
54292             return ["", ""];
54293         }
54294
54295         // build a map for all the columns
54296         var cs = [];
54297         for(var i = 0; i < colCount; i++){
54298             var name = cm.getDataIndex(i);
54299             cs[i] = {
54300                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54301                 renderer : cm.getRenderer(i),
54302                 id : cm.getColumnId(i),
54303                 locked : cm.isLocked(i)
54304             };
54305         }
54306
54307         startRow = startRow || 0;
54308         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54309
54310         // records to render
54311         var rs = ds.getRange(startRow, endRow);
54312
54313         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54314     },
54315
54316     // As much as I hate to duplicate code, this was branched because FireFox really hates
54317     // [].join("") on strings. The performance difference was substantial enough to
54318     // branch this function
54319     doRender : Roo.isGecko ?
54320             function(cs, rs, ds, startRow, colCount, stripe){
54321                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54322                 // buffers
54323                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54324                 
54325                 var hasListener = this.grid.hasListener('rowclass');
54326                 var rowcfg = {};
54327                 for(var j = 0, len = rs.length; j < len; j++){
54328                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54329                     for(var i = 0; i < colCount; i++){
54330                         c = cs[i];
54331                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54332                         p.id = c.id;
54333                         p.css = p.attr = "";
54334                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54335                         if(p.value == undefined || p.value === "") {
54336                             p.value = "&#160;";
54337                         }
54338                         if(r.editor){
54339                             p.css += 'x-grid-editable-cell';
54340                         }
54341                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
54342                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54343                         }
54344                         var markup = ct.apply(p);
54345                         if(!c.locked){
54346                             cb+= markup;
54347                         }else{
54348                             lcb+= markup;
54349                         }
54350                     }
54351                     var alt = [];
54352                     if(stripe && ((rowIndex+1) % 2 == 0)){
54353                         alt.push("x-grid-row-alt")
54354                     }
54355                     if(r.dirty){
54356                         alt.push(  " x-grid-dirty-row");
54357                     }
54358                     rp.cells = lcb;
54359                     if(this.getRowClass){
54360                         alt.push(this.getRowClass(r, rowIndex));
54361                     }
54362                     if (hasListener) {
54363                         rowcfg = {
54364                              
54365                             record: r,
54366                             rowIndex : rowIndex,
54367                             rowClass : ''
54368                         };
54369                         this.grid.fireEvent('rowclass', this, rowcfg);
54370                         alt.push(rowcfg.rowClass);
54371                     }
54372                     rp.alt = alt.join(" ");
54373                     lbuf+= rt.apply(rp);
54374                     rp.cells = cb;
54375                     buf+=  rt.apply(rp);
54376                 }
54377                 return [lbuf, buf];
54378             } :
54379             function(cs, rs, ds, startRow, colCount, stripe){
54380                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54381                 // buffers
54382                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54383                 var hasListener = this.grid.hasListener('rowclass');
54384  
54385                 var rowcfg = {};
54386                 for(var j = 0, len = rs.length; j < len; j++){
54387                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54388                     for(var i = 0; i < colCount; i++){
54389                         c = cs[i];
54390                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54391                         p.id = c.id;
54392                         p.css = p.attr = "";
54393                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54394                         if(p.value == undefined || p.value === "") {
54395                             p.value = "&#160;";
54396                         }
54397                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54398                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54399                         }
54400                         
54401                         var markup = ct.apply(p);
54402                         if(!c.locked){
54403                             cb[cb.length] = markup;
54404                         }else{
54405                             lcb[lcb.length] = markup;
54406                         }
54407                     }
54408                     var alt = [];
54409                     if(stripe && ((rowIndex+1) % 2 == 0)){
54410                         alt.push( "x-grid-row-alt");
54411                     }
54412                     if(r.dirty){
54413                         alt.push(" x-grid-dirty-row");
54414                     }
54415                     rp.cells = lcb;
54416                     if(this.getRowClass){
54417                         alt.push( this.getRowClass(r, rowIndex));
54418                     }
54419                     if (hasListener) {
54420                         rowcfg = {
54421                              
54422                             record: r,
54423                             rowIndex : rowIndex,
54424                             rowClass : ''
54425                         };
54426                         this.grid.fireEvent('rowclass', this, rowcfg);
54427                         alt.push(rowcfg.rowClass);
54428                     }
54429                     Roo.log(alt);
54430                     rp.alt = alt.join(" ");
54431                     rp.cells = lcb.join("");
54432                     lbuf[lbuf.length] = rt.apply(rp);
54433                     rp.cells = cb.join("");
54434                     buf[buf.length] =  rt.apply(rp);
54435                 }
54436                 return [lbuf.join(""), buf.join("")];
54437             },
54438
54439     renderBody : function(){
54440         var markup = this.renderRows();
54441         var bt = this.templates.body;
54442         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54443     },
54444
54445     /**
54446      * Refreshes the grid
54447      * @param {Boolean} headersToo
54448      */
54449     refresh : function(headersToo){
54450         this.fireEvent("beforerefresh", this);
54451         this.grid.stopEditing();
54452         var result = this.renderBody();
54453         this.lockedBody.update(result[0]);
54454         this.mainBody.update(result[1]);
54455         if(headersToo === true){
54456             this.updateHeaders();
54457             this.updateColumns();
54458             this.updateSplitters();
54459             this.updateHeaderSortState();
54460         }
54461         this.syncRowHeights();
54462         this.layout();
54463         this.fireEvent("refresh", this);
54464     },
54465
54466     handleColumnMove : function(cm, oldIndex, newIndex){
54467         this.indexMap = null;
54468         var s = this.getScrollState();
54469         this.refresh(true);
54470         this.restoreScroll(s);
54471         this.afterMove(newIndex);
54472     },
54473
54474     afterMove : function(colIndex){
54475         if(this.enableMoveAnim && Roo.enableFx){
54476             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54477         }
54478         // if multisort - fix sortOrder, and reload..
54479         if (this.grid.dataSource.multiSort) {
54480             // the we can call sort again..
54481             var dm = this.grid.dataSource;
54482             var cm = this.grid.colModel;
54483             var so = [];
54484             for(var i = 0; i < cm.config.length; i++ ) {
54485                 
54486                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54487                     continue; // dont' bother, it's not in sort list or being set.
54488                 }
54489                 
54490                 so.push(cm.config[i].dataIndex);
54491             };
54492             dm.sortOrder = so;
54493             dm.load(dm.lastOptions);
54494             
54495             
54496         }
54497         
54498     },
54499
54500     updateCell : function(dm, rowIndex, dataIndex){
54501         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54502         if(typeof colIndex == "undefined"){ // not present in grid
54503             return;
54504         }
54505         var cm = this.grid.colModel;
54506         var cell = this.getCell(rowIndex, colIndex);
54507         var cellText = this.getCellText(rowIndex, colIndex);
54508
54509         var p = {
54510             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54511             id : cm.getColumnId(colIndex),
54512             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54513         };
54514         var renderer = cm.getRenderer(colIndex);
54515         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54516         if(typeof val == "undefined" || val === "") {
54517             val = "&#160;";
54518         }
54519         cellText.innerHTML = val;
54520         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54521         this.syncRowHeights(rowIndex, rowIndex);
54522     },
54523
54524     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54525         var maxWidth = 0;
54526         if(this.grid.autoSizeHeaders){
54527             var h = this.getHeaderCellMeasure(colIndex);
54528             maxWidth = Math.max(maxWidth, h.scrollWidth);
54529         }
54530         var tb, index;
54531         if(this.cm.isLocked(colIndex)){
54532             tb = this.getLockedTable();
54533             index = colIndex;
54534         }else{
54535             tb = this.getBodyTable();
54536             index = colIndex - this.cm.getLockedCount();
54537         }
54538         if(tb && tb.rows){
54539             var rows = tb.rows;
54540             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54541             for(var i = 0; i < stopIndex; i++){
54542                 var cell = rows[i].childNodes[index].firstChild;
54543                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54544             }
54545         }
54546         return maxWidth + /*margin for error in IE*/ 5;
54547     },
54548     /**
54549      * Autofit a column to its content.
54550      * @param {Number} colIndex
54551      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54552      */
54553      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54554          if(this.cm.isHidden(colIndex)){
54555              return; // can't calc a hidden column
54556          }
54557         if(forceMinSize){
54558             var cid = this.cm.getColumnId(colIndex);
54559             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54560            if(this.grid.autoSizeHeaders){
54561                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54562            }
54563         }
54564         var newWidth = this.calcColumnWidth(colIndex);
54565         this.cm.setColumnWidth(colIndex,
54566             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54567         if(!suppressEvent){
54568             this.grid.fireEvent("columnresize", colIndex, newWidth);
54569         }
54570     },
54571
54572     /**
54573      * Autofits all columns to their content and then expands to fit any extra space in the grid
54574      */
54575      autoSizeColumns : function(){
54576         var cm = this.grid.colModel;
54577         var colCount = cm.getColumnCount();
54578         for(var i = 0; i < colCount; i++){
54579             this.autoSizeColumn(i, true, true);
54580         }
54581         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54582             this.fitColumns();
54583         }else{
54584             this.updateColumns();
54585             this.layout();
54586         }
54587     },
54588
54589     /**
54590      * Autofits all columns to the grid's width proportionate with their current size
54591      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54592      */
54593     fitColumns : function(reserveScrollSpace){
54594         var cm = this.grid.colModel;
54595         var colCount = cm.getColumnCount();
54596         var cols = [];
54597         var width = 0;
54598         var i, w;
54599         for (i = 0; i < colCount; i++){
54600             if(!cm.isHidden(i) && !cm.isFixed(i)){
54601                 w = cm.getColumnWidth(i);
54602                 cols.push(i);
54603                 cols.push(w);
54604                 width += w;
54605             }
54606         }
54607         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54608         if(reserveScrollSpace){
54609             avail -= 17;
54610         }
54611         var frac = (avail - cm.getTotalWidth())/width;
54612         while (cols.length){
54613             w = cols.pop();
54614             i = cols.pop();
54615             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54616         }
54617         this.updateColumns();
54618         this.layout();
54619     },
54620
54621     onRowSelect : function(rowIndex){
54622         var row = this.getRowComposite(rowIndex);
54623         row.addClass("x-grid-row-selected");
54624     },
54625
54626     onRowDeselect : function(rowIndex){
54627         var row = this.getRowComposite(rowIndex);
54628         row.removeClass("x-grid-row-selected");
54629     },
54630
54631     onCellSelect : function(row, col){
54632         var cell = this.getCell(row, col);
54633         if(cell){
54634             Roo.fly(cell).addClass("x-grid-cell-selected");
54635         }
54636     },
54637
54638     onCellDeselect : function(row, col){
54639         var cell = this.getCell(row, col);
54640         if(cell){
54641             Roo.fly(cell).removeClass("x-grid-cell-selected");
54642         }
54643     },
54644
54645     updateHeaderSortState : function(){
54646         
54647         // sort state can be single { field: xxx, direction : yyy}
54648         // or   { xxx=>ASC , yyy : DESC ..... }
54649         
54650         var mstate = {};
54651         if (!this.ds.multiSort) { 
54652             var state = this.ds.getSortState();
54653             if(!state){
54654                 return;
54655             }
54656             mstate[state.field] = state.direction;
54657             // FIXME... - this is not used here.. but might be elsewhere..
54658             this.sortState = state;
54659             
54660         } else {
54661             mstate = this.ds.sortToggle;
54662         }
54663         //remove existing sort classes..
54664         
54665         var sc = this.sortClasses;
54666         var hds = this.el.select(this.headerSelector).removeClass(sc);
54667         
54668         for(var f in mstate) {
54669         
54670             var sortColumn = this.cm.findColumnIndex(f);
54671             
54672             if(sortColumn != -1){
54673                 var sortDir = mstate[f];        
54674                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54675             }
54676         }
54677         
54678          
54679         
54680     },
54681
54682
54683     handleHeaderClick : function(g, index,e){
54684         
54685         Roo.log("header click");
54686         
54687         if (Roo.isTouch) {
54688             // touch events on header are handled by context
54689             this.handleHdCtx(g,index,e);
54690             return;
54691         }
54692         
54693         
54694         if(this.headersDisabled){
54695             return;
54696         }
54697         var dm = g.dataSource, cm = g.colModel;
54698         if(!cm.isSortable(index)){
54699             return;
54700         }
54701         g.stopEditing();
54702         
54703         if (dm.multiSort) {
54704             // update the sortOrder
54705             var so = [];
54706             for(var i = 0; i < cm.config.length; i++ ) {
54707                 
54708                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54709                     continue; // dont' bother, it's not in sort list or being set.
54710                 }
54711                 
54712                 so.push(cm.config[i].dataIndex);
54713             };
54714             dm.sortOrder = so;
54715         }
54716         
54717         
54718         dm.sort(cm.getDataIndex(index));
54719     },
54720
54721
54722     destroy : function(){
54723         if(this.colMenu){
54724             this.colMenu.removeAll();
54725             Roo.menu.MenuMgr.unregister(this.colMenu);
54726             this.colMenu.getEl().remove();
54727             delete this.colMenu;
54728         }
54729         if(this.hmenu){
54730             this.hmenu.removeAll();
54731             Roo.menu.MenuMgr.unregister(this.hmenu);
54732             this.hmenu.getEl().remove();
54733             delete this.hmenu;
54734         }
54735         if(this.grid.enableColumnMove){
54736             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54737             if(dds){
54738                 for(var dd in dds){
54739                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54740                         var elid = dds[dd].dragElId;
54741                         dds[dd].unreg();
54742                         Roo.get(elid).remove();
54743                     } else if(dds[dd].config.isTarget){
54744                         dds[dd].proxyTop.remove();
54745                         dds[dd].proxyBottom.remove();
54746                         dds[dd].unreg();
54747                     }
54748                     if(Roo.dd.DDM.locationCache[dd]){
54749                         delete Roo.dd.DDM.locationCache[dd];
54750                     }
54751                 }
54752                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54753             }
54754         }
54755         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54756         this.bind(null, null);
54757         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54758     },
54759
54760     handleLockChange : function(){
54761         this.refresh(true);
54762     },
54763
54764     onDenyColumnLock : function(){
54765
54766     },
54767
54768     onDenyColumnHide : function(){
54769
54770     },
54771
54772     handleHdMenuClick : function(item){
54773         var index = this.hdCtxIndex;
54774         var cm = this.cm, ds = this.ds;
54775         switch(item.id){
54776             case "asc":
54777                 ds.sort(cm.getDataIndex(index), "ASC");
54778                 break;
54779             case "desc":
54780                 ds.sort(cm.getDataIndex(index), "DESC");
54781                 break;
54782             case "lock":
54783                 var lc = cm.getLockedCount();
54784                 if(cm.getColumnCount(true) <= lc+1){
54785                     this.onDenyColumnLock();
54786                     return;
54787                 }
54788                 if(lc != index){
54789                     cm.setLocked(index, true, true);
54790                     cm.moveColumn(index, lc);
54791                     this.grid.fireEvent("columnmove", index, lc);
54792                 }else{
54793                     cm.setLocked(index, true);
54794                 }
54795             break;
54796             case "unlock":
54797                 var lc = cm.getLockedCount();
54798                 if((lc-1) != index){
54799                     cm.setLocked(index, false, true);
54800                     cm.moveColumn(index, lc-1);
54801                     this.grid.fireEvent("columnmove", index, lc-1);
54802                 }else{
54803                     cm.setLocked(index, false);
54804                 }
54805             break;
54806             case 'wider': // used to expand cols on touch..
54807             case 'narrow':
54808                 var cw = cm.getColumnWidth(index);
54809                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54810                 cw = Math.max(0, cw);
54811                 cw = Math.min(cw,4000);
54812                 cm.setColumnWidth(index, cw);
54813                 break;
54814                 
54815             default:
54816                 index = cm.getIndexById(item.id.substr(4));
54817                 if(index != -1){
54818                     if(item.checked && cm.getColumnCount(true) <= 1){
54819                         this.onDenyColumnHide();
54820                         return false;
54821                     }
54822                     cm.setHidden(index, item.checked);
54823                 }
54824         }
54825         return true;
54826     },
54827
54828     beforeColMenuShow : function(){
54829         var cm = this.cm,  colCount = cm.getColumnCount();
54830         this.colMenu.removeAll();
54831         for(var i = 0; i < colCount; i++){
54832             this.colMenu.add(new Roo.menu.CheckItem({
54833                 id: "col-"+cm.getColumnId(i),
54834                 text: cm.getColumnHeader(i),
54835                 checked: !cm.isHidden(i),
54836                 hideOnClick:false
54837             }));
54838         }
54839     },
54840
54841     handleHdCtx : function(g, index, e){
54842         e.stopEvent();
54843         var hd = this.getHeaderCell(index);
54844         this.hdCtxIndex = index;
54845         var ms = this.hmenu.items, cm = this.cm;
54846         ms.get("asc").setDisabled(!cm.isSortable(index));
54847         ms.get("desc").setDisabled(!cm.isSortable(index));
54848         if(this.grid.enableColLock !== false){
54849             ms.get("lock").setDisabled(cm.isLocked(index));
54850             ms.get("unlock").setDisabled(!cm.isLocked(index));
54851         }
54852         this.hmenu.show(hd, "tl-bl");
54853     },
54854
54855     handleHdOver : function(e){
54856         var hd = this.findHeaderCell(e.getTarget());
54857         if(hd && !this.headersDisabled){
54858             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54859                this.fly(hd).addClass("x-grid-hd-over");
54860             }
54861         }
54862     },
54863
54864     handleHdOut : function(e){
54865         var hd = this.findHeaderCell(e.getTarget());
54866         if(hd){
54867             this.fly(hd).removeClass("x-grid-hd-over");
54868         }
54869     },
54870
54871     handleSplitDblClick : function(e, t){
54872         var i = this.getCellIndex(t);
54873         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54874             this.autoSizeColumn(i, true);
54875             this.layout();
54876         }
54877     },
54878
54879     render : function(){
54880
54881         var cm = this.cm;
54882         var colCount = cm.getColumnCount();
54883
54884         if(this.grid.monitorWindowResize === true){
54885             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54886         }
54887         var header = this.renderHeaders();
54888         var body = this.templates.body.apply({rows:""});
54889         var html = this.templates.master.apply({
54890             lockedBody: body,
54891             body: body,
54892             lockedHeader: header[0],
54893             header: header[1]
54894         });
54895
54896         //this.updateColumns();
54897
54898         this.grid.getGridEl().dom.innerHTML = html;
54899
54900         this.initElements();
54901         
54902         // a kludge to fix the random scolling effect in webkit
54903         this.el.on("scroll", function() {
54904             this.el.dom.scrollTop=0; // hopefully not recursive..
54905         },this);
54906
54907         this.scroller.on("scroll", this.handleScroll, this);
54908         this.lockedBody.on("mousewheel", this.handleWheel, this);
54909         this.mainBody.on("mousewheel", this.handleWheel, this);
54910
54911         this.mainHd.on("mouseover", this.handleHdOver, this);
54912         this.mainHd.on("mouseout", this.handleHdOut, this);
54913         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54914                 {delegate: "."+this.splitClass});
54915
54916         this.lockedHd.on("mouseover", this.handleHdOver, this);
54917         this.lockedHd.on("mouseout", this.handleHdOut, this);
54918         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54919                 {delegate: "."+this.splitClass});
54920
54921         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54922             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54923         }
54924
54925         this.updateSplitters();
54926
54927         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54928             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54929             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54930         }
54931
54932         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54933             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54934             this.hmenu.add(
54935                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54936                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54937             );
54938             if(this.grid.enableColLock !== false){
54939                 this.hmenu.add('-',
54940                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54941                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54942                 );
54943             }
54944             if (Roo.isTouch) {
54945                  this.hmenu.add('-',
54946                     {id:"wider", text: this.columnsWiderText},
54947                     {id:"narrow", text: this.columnsNarrowText }
54948                 );
54949                 
54950                  
54951             }
54952             
54953             if(this.grid.enableColumnHide !== false){
54954
54955                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54956                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54957                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54958
54959                 this.hmenu.add('-',
54960                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54961                 );
54962             }
54963             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54964
54965             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54966         }
54967
54968         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54969             this.dd = new Roo.grid.GridDragZone(this.grid, {
54970                 ddGroup : this.grid.ddGroup || 'GridDD'
54971             });
54972             
54973         }
54974
54975         /*
54976         for(var i = 0; i < colCount; i++){
54977             if(cm.isHidden(i)){
54978                 this.hideColumn(i);
54979             }
54980             if(cm.config[i].align){
54981                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54982                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54983             }
54984         }*/
54985         
54986         this.updateHeaderSortState();
54987
54988         this.beforeInitialResize();
54989         this.layout(true);
54990
54991         // two part rendering gives faster view to the user
54992         this.renderPhase2.defer(1, this);
54993     },
54994
54995     renderPhase2 : function(){
54996         // render the rows now
54997         this.refresh();
54998         if(this.grid.autoSizeColumns){
54999             this.autoSizeColumns();
55000         }
55001     },
55002
55003     beforeInitialResize : function(){
55004
55005     },
55006
55007     onColumnSplitterMoved : function(i, w){
55008         this.userResized = true;
55009         var cm = this.grid.colModel;
55010         cm.setColumnWidth(i, w, true);
55011         var cid = cm.getColumnId(i);
55012         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
55013         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
55014         this.updateSplitters();
55015         this.layout();
55016         this.grid.fireEvent("columnresize", i, w);
55017     },
55018
55019     syncRowHeights : function(startIndex, endIndex){
55020         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
55021             startIndex = startIndex || 0;
55022             var mrows = this.getBodyTable().rows;
55023             var lrows = this.getLockedTable().rows;
55024             var len = mrows.length-1;
55025             endIndex = Math.min(endIndex || len, len);
55026             for(var i = startIndex; i <= endIndex; i++){
55027                 var m = mrows[i], l = lrows[i];
55028                 var h = Math.max(m.offsetHeight, l.offsetHeight);
55029                 m.style.height = l.style.height = h + "px";
55030             }
55031         }
55032     },
55033
55034     layout : function(initialRender, is2ndPass){
55035         var g = this.grid;
55036         var auto = g.autoHeight;
55037         var scrollOffset = 16;
55038         var c = g.getGridEl(), cm = this.cm,
55039                 expandCol = g.autoExpandColumn,
55040                 gv = this;
55041         //c.beginMeasure();
55042
55043         if(!c.dom.offsetWidth){ // display:none?
55044             if(initialRender){
55045                 this.lockedWrap.show();
55046                 this.mainWrap.show();
55047             }
55048             return;
55049         }
55050
55051         var hasLock = this.cm.isLocked(0);
55052
55053         var tbh = this.headerPanel.getHeight();
55054         var bbh = this.footerPanel.getHeight();
55055
55056         if(auto){
55057             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
55058             var newHeight = ch + c.getBorderWidth("tb");
55059             if(g.maxHeight){
55060                 newHeight = Math.min(g.maxHeight, newHeight);
55061             }
55062             c.setHeight(newHeight);
55063         }
55064
55065         if(g.autoWidth){
55066             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
55067         }
55068
55069         var s = this.scroller;
55070
55071         var csize = c.getSize(true);
55072
55073         this.el.setSize(csize.width, csize.height);
55074
55075         this.headerPanel.setWidth(csize.width);
55076         this.footerPanel.setWidth(csize.width);
55077
55078         var hdHeight = this.mainHd.getHeight();
55079         var vw = csize.width;
55080         var vh = csize.height - (tbh + bbh);
55081
55082         s.setSize(vw, vh);
55083
55084         var bt = this.getBodyTable();
55085         var ltWidth = hasLock ?
55086                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
55087
55088         var scrollHeight = bt.offsetHeight;
55089         var scrollWidth = ltWidth + bt.offsetWidth;
55090         var vscroll = false, hscroll = false;
55091
55092         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
55093
55094         var lw = this.lockedWrap, mw = this.mainWrap;
55095         var lb = this.lockedBody, mb = this.mainBody;
55096
55097         setTimeout(function(){
55098             var t = s.dom.offsetTop;
55099             var w = s.dom.clientWidth,
55100                 h = s.dom.clientHeight;
55101
55102             lw.setTop(t);
55103             lw.setSize(ltWidth, h);
55104
55105             mw.setLeftTop(ltWidth, t);
55106             mw.setSize(w-ltWidth, h);
55107
55108             lb.setHeight(h-hdHeight);
55109             mb.setHeight(h-hdHeight);
55110
55111             if(is2ndPass !== true && !gv.userResized && expandCol){
55112                 // high speed resize without full column calculation
55113                 
55114                 var ci = cm.getIndexById(expandCol);
55115                 if (ci < 0) {
55116                     ci = cm.findColumnIndex(expandCol);
55117                 }
55118                 ci = Math.max(0, ci); // make sure it's got at least the first col.
55119                 var expandId = cm.getColumnId(ci);
55120                 var  tw = cm.getTotalWidth(false);
55121                 var currentWidth = cm.getColumnWidth(ci);
55122                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
55123                 if(currentWidth != cw){
55124                     cm.setColumnWidth(ci, cw, true);
55125                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55126                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55127                     gv.updateSplitters();
55128                     gv.layout(false, true);
55129                 }
55130             }
55131
55132             if(initialRender){
55133                 lw.show();
55134                 mw.show();
55135             }
55136             //c.endMeasure();
55137         }, 10);
55138     },
55139
55140     onWindowResize : function(){
55141         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55142             return;
55143         }
55144         this.layout();
55145     },
55146
55147     appendFooter : function(parentEl){
55148         return null;
55149     },
55150
55151     sortAscText : "Sort Ascending",
55152     sortDescText : "Sort Descending",
55153     lockText : "Lock Column",
55154     unlockText : "Unlock Column",
55155     columnsText : "Columns",
55156  
55157     columnsWiderText : "Wider",
55158     columnsNarrowText : "Thinner"
55159 });
55160
55161
55162 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55163     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55164     this.proxy.el.addClass('x-grid3-col-dd');
55165 };
55166
55167 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55168     handleMouseDown : function(e){
55169
55170     },
55171
55172     callHandleMouseDown : function(e){
55173         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55174     }
55175 });
55176 /*
55177  * Based on:
55178  * Ext JS Library 1.1.1
55179  * Copyright(c) 2006-2007, Ext JS, LLC.
55180  *
55181  * Originally Released Under LGPL - original licence link has changed is not relivant.
55182  *
55183  * Fork - LGPL
55184  * <script type="text/javascript">
55185  */
55186  
55187 // private
55188 // This is a support class used internally by the Grid components
55189 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55190     this.grid = grid;
55191     this.view = grid.getView();
55192     this.proxy = this.view.resizeProxy;
55193     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55194         "gridSplitters" + this.grid.getGridEl().id, {
55195         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55196     });
55197     this.setHandleElId(Roo.id(hd));
55198     this.setOuterHandleElId(Roo.id(hd2));
55199     this.scroll = false;
55200 };
55201 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55202     fly: Roo.Element.fly,
55203
55204     b4StartDrag : function(x, y){
55205         this.view.headersDisabled = true;
55206         this.proxy.setHeight(this.view.mainWrap.getHeight());
55207         var w = this.cm.getColumnWidth(this.cellIndex);
55208         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55209         this.resetConstraints();
55210         this.setXConstraint(minw, 1000);
55211         this.setYConstraint(0, 0);
55212         this.minX = x - minw;
55213         this.maxX = x + 1000;
55214         this.startPos = x;
55215         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55216     },
55217
55218
55219     handleMouseDown : function(e){
55220         ev = Roo.EventObject.setEvent(e);
55221         var t = this.fly(ev.getTarget());
55222         if(t.hasClass("x-grid-split")){
55223             this.cellIndex = this.view.getCellIndex(t.dom);
55224             this.split = t.dom;
55225             this.cm = this.grid.colModel;
55226             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55227                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55228             }
55229         }
55230     },
55231
55232     endDrag : function(e){
55233         this.view.headersDisabled = false;
55234         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55235         var diff = endX - this.startPos;
55236         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55237     },
55238
55239     autoOffset : function(){
55240         this.setDelta(0,0);
55241     }
55242 });/*
55243  * Based on:
55244  * Ext JS Library 1.1.1
55245  * Copyright(c) 2006-2007, Ext JS, LLC.
55246  *
55247  * Originally Released Under LGPL - original licence link has changed is not relivant.
55248  *
55249  * Fork - LGPL
55250  * <script type="text/javascript">
55251  */
55252  
55253 // private
55254 // This is a support class used internally by the Grid components
55255 Roo.grid.GridDragZone = function(grid, config){
55256     this.view = grid.getView();
55257     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55258     if(this.view.lockedBody){
55259         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55260         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55261     }
55262     this.scroll = false;
55263     this.grid = grid;
55264     this.ddel = document.createElement('div');
55265     this.ddel.className = 'x-grid-dd-wrap';
55266 };
55267
55268 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55269     ddGroup : "GridDD",
55270
55271     getDragData : function(e){
55272         var t = Roo.lib.Event.getTarget(e);
55273         var rowIndex = this.view.findRowIndex(t);
55274         var sm = this.grid.selModel;
55275             
55276         //Roo.log(rowIndex);
55277         
55278         if (sm.getSelectedCell) {
55279             // cell selection..
55280             if (!sm.getSelectedCell()) {
55281                 return false;
55282             }
55283             if (rowIndex != sm.getSelectedCell()[0]) {
55284                 return false;
55285             }
55286         
55287         }
55288         
55289         if(rowIndex !== false){
55290             
55291             // if editorgrid.. 
55292             
55293             
55294             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55295                
55296             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55297               //  
55298             //}
55299             if (e.hasModifier()){
55300                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55301             }
55302             
55303             Roo.log("getDragData");
55304             
55305             return {
55306                 grid: this.grid,
55307                 ddel: this.ddel,
55308                 rowIndex: rowIndex,
55309                 selections:sm.getSelections ? sm.getSelections() : (
55310                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55311                 )
55312             };
55313         }
55314         return false;
55315     },
55316
55317     onInitDrag : function(e){
55318         var data = this.dragData;
55319         this.ddel.innerHTML = this.grid.getDragDropText();
55320         this.proxy.update(this.ddel);
55321         // fire start drag?
55322     },
55323
55324     afterRepair : function(){
55325         this.dragging = false;
55326     },
55327
55328     getRepairXY : function(e, data){
55329         return false;
55330     },
55331
55332     onEndDrag : function(data, e){
55333         // fire end drag?
55334     },
55335
55336     onValidDrop : function(dd, e, id){
55337         // fire drag drop?
55338         this.hideProxy();
55339     },
55340
55341     beforeInvalidDrop : function(e, id){
55342
55343     }
55344 });/*
55345  * Based on:
55346  * Ext JS Library 1.1.1
55347  * Copyright(c) 2006-2007, Ext JS, LLC.
55348  *
55349  * Originally Released Under LGPL - original licence link has changed is not relivant.
55350  *
55351  * Fork - LGPL
55352  * <script type="text/javascript">
55353  */
55354  
55355
55356 /**
55357  * @class Roo.grid.ColumnModel
55358  * @extends Roo.util.Observable
55359  * This is the default implementation of a ColumnModel used by the Grid. It defines
55360  * the columns in the grid.
55361  * <br>Usage:<br>
55362  <pre><code>
55363  var colModel = new Roo.grid.ColumnModel([
55364         {header: "Ticker", width: 60, sortable: true, locked: true},
55365         {header: "Company Name", width: 150, sortable: true},
55366         {header: "Market Cap.", width: 100, sortable: true},
55367         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55368         {header: "Employees", width: 100, sortable: true, resizable: false}
55369  ]);
55370  </code></pre>
55371  * <p>
55372  
55373  * The config options listed for this class are options which may appear in each
55374  * individual column definition.
55375  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55376  * @constructor
55377  * @param {Object} config An Array of column config objects. See this class's
55378  * config objects for details.
55379 */
55380 Roo.grid.ColumnModel = function(config){
55381         /**
55382      * The config passed into the constructor
55383      */
55384     this.config = config;
55385     this.lookup = {};
55386
55387     // if no id, create one
55388     // if the column does not have a dataIndex mapping,
55389     // map it to the order it is in the config
55390     for(var i = 0, len = config.length; i < len; i++){
55391         var c = config[i];
55392         if(typeof c.dataIndex == "undefined"){
55393             c.dataIndex = i;
55394         }
55395         if(typeof c.renderer == "string"){
55396             c.renderer = Roo.util.Format[c.renderer];
55397         }
55398         if(typeof c.id == "undefined"){
55399             c.id = Roo.id();
55400         }
55401         if(c.editor && c.editor.xtype){
55402             c.editor  = Roo.factory(c.editor, Roo.grid);
55403         }
55404         if(c.editor && c.editor.isFormField){
55405             c.editor = new Roo.grid.GridEditor(c.editor);
55406         }
55407         this.lookup[c.id] = c;
55408     }
55409
55410     /**
55411      * The width of columns which have no width specified (defaults to 100)
55412      * @type Number
55413      */
55414     this.defaultWidth = 100;
55415
55416     /**
55417      * Default sortable of columns which have no sortable specified (defaults to false)
55418      * @type Boolean
55419      */
55420     this.defaultSortable = false;
55421
55422     this.addEvents({
55423         /**
55424              * @event widthchange
55425              * Fires when the width of a column changes.
55426              * @param {ColumnModel} this
55427              * @param {Number} columnIndex The column index
55428              * @param {Number} newWidth The new width
55429              */
55430             "widthchange": true,
55431         /**
55432              * @event headerchange
55433              * Fires when the text of a header changes.
55434              * @param {ColumnModel} this
55435              * @param {Number} columnIndex The column index
55436              * @param {Number} newText The new header text
55437              */
55438             "headerchange": true,
55439         /**
55440              * @event hiddenchange
55441              * Fires when a column is hidden or "unhidden".
55442              * @param {ColumnModel} this
55443              * @param {Number} columnIndex The column index
55444              * @param {Boolean} hidden true if hidden, false otherwise
55445              */
55446             "hiddenchange": true,
55447             /**
55448          * @event columnmoved
55449          * Fires when a column is moved.
55450          * @param {ColumnModel} this
55451          * @param {Number} oldIndex
55452          * @param {Number} newIndex
55453          */
55454         "columnmoved" : true,
55455         /**
55456          * @event columlockchange
55457          * Fires when a column's locked state is changed
55458          * @param {ColumnModel} this
55459          * @param {Number} colIndex
55460          * @param {Boolean} locked true if locked
55461          */
55462         "columnlockchange" : true
55463     });
55464     Roo.grid.ColumnModel.superclass.constructor.call(this);
55465 };
55466 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55467     /**
55468      * @cfg {String} header The header text to display in the Grid view.
55469      */
55470     /**
55471      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55472      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55473      * specified, the column's index is used as an index into the Record's data Array.
55474      */
55475     /**
55476      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55477      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55478      */
55479     /**
55480      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55481      * Defaults to the value of the {@link #defaultSortable} property.
55482      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55483      */
55484     /**
55485      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55486      */
55487     /**
55488      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55489      */
55490     /**
55491      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55492      */
55493     /**
55494      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55495      */
55496     /**
55497      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55498      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55499      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55500      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55501      */
55502        /**
55503      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55504      */
55505     /**
55506      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55507      */
55508     /**
55509      * @cfg {String} cursor (Optional)
55510      */
55511     /**
55512      * @cfg {String} tooltip (Optional)
55513      */
55514     /**
55515      * @cfg {Number} xs (Optional)
55516      */
55517     /**
55518      * @cfg {Number} sm (Optional)
55519      */
55520     /**
55521      * @cfg {Number} md (Optional)
55522      */
55523     /**
55524      * @cfg {Number} lg (Optional)
55525      */
55526     /**
55527      * Returns the id of the column at the specified index.
55528      * @param {Number} index The column index
55529      * @return {String} the id
55530      */
55531     getColumnId : function(index){
55532         return this.config[index].id;
55533     },
55534
55535     /**
55536      * Returns the column for a specified id.
55537      * @param {String} id The column id
55538      * @return {Object} the column
55539      */
55540     getColumnById : function(id){
55541         return this.lookup[id];
55542     },
55543
55544     
55545     /**
55546      * Returns the column for a specified dataIndex.
55547      * @param {String} dataIndex The column dataIndex
55548      * @return {Object|Boolean} the column or false if not found
55549      */
55550     getColumnByDataIndex: function(dataIndex){
55551         var index = this.findColumnIndex(dataIndex);
55552         return index > -1 ? this.config[index] : false;
55553     },
55554     
55555     /**
55556      * Returns the index for a specified column id.
55557      * @param {String} id The column id
55558      * @return {Number} the index, or -1 if not found
55559      */
55560     getIndexById : function(id){
55561         for(var i = 0, len = this.config.length; i < len; i++){
55562             if(this.config[i].id == id){
55563                 return i;
55564             }
55565         }
55566         return -1;
55567     },
55568     
55569     /**
55570      * Returns the index for a specified column dataIndex.
55571      * @param {String} dataIndex The column dataIndex
55572      * @return {Number} the index, or -1 if not found
55573      */
55574     
55575     findColumnIndex : function(dataIndex){
55576         for(var i = 0, len = this.config.length; i < len; i++){
55577             if(this.config[i].dataIndex == dataIndex){
55578                 return i;
55579             }
55580         }
55581         return -1;
55582     },
55583     
55584     
55585     moveColumn : function(oldIndex, newIndex){
55586         var c = this.config[oldIndex];
55587         this.config.splice(oldIndex, 1);
55588         this.config.splice(newIndex, 0, c);
55589         this.dataMap = null;
55590         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55591     },
55592
55593     isLocked : function(colIndex){
55594         return this.config[colIndex].locked === true;
55595     },
55596
55597     setLocked : function(colIndex, value, suppressEvent){
55598         if(this.isLocked(colIndex) == value){
55599             return;
55600         }
55601         this.config[colIndex].locked = value;
55602         if(!suppressEvent){
55603             this.fireEvent("columnlockchange", this, colIndex, value);
55604         }
55605     },
55606
55607     getTotalLockedWidth : function(){
55608         var totalWidth = 0;
55609         for(var i = 0; i < this.config.length; i++){
55610             if(this.isLocked(i) && !this.isHidden(i)){
55611                 this.totalWidth += this.getColumnWidth(i);
55612             }
55613         }
55614         return totalWidth;
55615     },
55616
55617     getLockedCount : function(){
55618         for(var i = 0, len = this.config.length; i < len; i++){
55619             if(!this.isLocked(i)){
55620                 return i;
55621             }
55622         }
55623     },
55624
55625     /**
55626      * Returns the number of columns.
55627      * @return {Number}
55628      */
55629     getColumnCount : function(visibleOnly){
55630         if(visibleOnly === true){
55631             var c = 0;
55632             for(var i = 0, len = this.config.length; i < len; i++){
55633                 if(!this.isHidden(i)){
55634                     c++;
55635                 }
55636             }
55637             return c;
55638         }
55639         return this.config.length;
55640     },
55641
55642     /**
55643      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55644      * @param {Function} fn
55645      * @param {Object} scope (optional)
55646      * @return {Array} result
55647      */
55648     getColumnsBy : function(fn, scope){
55649         var r = [];
55650         for(var i = 0, len = this.config.length; i < len; i++){
55651             var c = this.config[i];
55652             if(fn.call(scope||this, c, i) === true){
55653                 r[r.length] = c;
55654             }
55655         }
55656         return r;
55657     },
55658
55659     /**
55660      * Returns true if the specified column is sortable.
55661      * @param {Number} col The column index
55662      * @return {Boolean}
55663      */
55664     isSortable : function(col){
55665         if(typeof this.config[col].sortable == "undefined"){
55666             return this.defaultSortable;
55667         }
55668         return this.config[col].sortable;
55669     },
55670
55671     /**
55672      * Returns the rendering (formatting) function defined for the column.
55673      * @param {Number} col The column index.
55674      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55675      */
55676     getRenderer : function(col){
55677         if(!this.config[col].renderer){
55678             return Roo.grid.ColumnModel.defaultRenderer;
55679         }
55680         return this.config[col].renderer;
55681     },
55682
55683     /**
55684      * Sets the rendering (formatting) function for a column.
55685      * @param {Number} col The column index
55686      * @param {Function} fn The function to use to process the cell's raw data
55687      * to return HTML markup for the grid view. The render function is called with
55688      * the following parameters:<ul>
55689      * <li>Data value.</li>
55690      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55691      * <li>css A CSS style string to apply to the table cell.</li>
55692      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55693      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55694      * <li>Row index</li>
55695      * <li>Column index</li>
55696      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55697      */
55698     setRenderer : function(col, fn){
55699         this.config[col].renderer = fn;
55700     },
55701
55702     /**
55703      * Returns the width for the specified column.
55704      * @param {Number} col The column index
55705      * @return {Number}
55706      */
55707     getColumnWidth : function(col){
55708         return this.config[col].width * 1 || this.defaultWidth;
55709     },
55710
55711     /**
55712      * Sets the width for a column.
55713      * @param {Number} col The column index
55714      * @param {Number} width The new width
55715      */
55716     setColumnWidth : function(col, width, suppressEvent){
55717         this.config[col].width = width;
55718         this.totalWidth = null;
55719         if(!suppressEvent){
55720              this.fireEvent("widthchange", this, col, width);
55721         }
55722     },
55723
55724     /**
55725      * Returns the total width of all columns.
55726      * @param {Boolean} includeHidden True to include hidden column widths
55727      * @return {Number}
55728      */
55729     getTotalWidth : function(includeHidden){
55730         if(!this.totalWidth){
55731             this.totalWidth = 0;
55732             for(var i = 0, len = this.config.length; i < len; i++){
55733                 if(includeHidden || !this.isHidden(i)){
55734                     this.totalWidth += this.getColumnWidth(i);
55735                 }
55736             }
55737         }
55738         return this.totalWidth;
55739     },
55740
55741     /**
55742      * Returns the header for the specified column.
55743      * @param {Number} col The column index
55744      * @return {String}
55745      */
55746     getColumnHeader : function(col){
55747         return this.config[col].header;
55748     },
55749
55750     /**
55751      * Sets the header for a column.
55752      * @param {Number} col The column index
55753      * @param {String} header The new header
55754      */
55755     setColumnHeader : function(col, header){
55756         this.config[col].header = header;
55757         this.fireEvent("headerchange", this, col, header);
55758     },
55759
55760     /**
55761      * Returns the tooltip for the specified column.
55762      * @param {Number} col The column index
55763      * @return {String}
55764      */
55765     getColumnTooltip : function(col){
55766             return this.config[col].tooltip;
55767     },
55768     /**
55769      * Sets the tooltip for a column.
55770      * @param {Number} col The column index
55771      * @param {String} tooltip The new tooltip
55772      */
55773     setColumnTooltip : function(col, tooltip){
55774             this.config[col].tooltip = tooltip;
55775     },
55776
55777     /**
55778      * Returns the dataIndex for the specified column.
55779      * @param {Number} col The column index
55780      * @return {Number}
55781      */
55782     getDataIndex : function(col){
55783         return this.config[col].dataIndex;
55784     },
55785
55786     /**
55787      * Sets the dataIndex for a column.
55788      * @param {Number} col The column index
55789      * @param {Number} dataIndex The new dataIndex
55790      */
55791     setDataIndex : function(col, dataIndex){
55792         this.config[col].dataIndex = dataIndex;
55793     },
55794
55795     
55796     
55797     /**
55798      * Returns true if the cell is editable.
55799      * @param {Number} colIndex The column index
55800      * @param {Number} rowIndex The row index
55801      * @return {Boolean}
55802      */
55803     isCellEditable : function(colIndex, rowIndex){
55804         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55805     },
55806
55807     /**
55808      * Returns the editor defined for the cell/column.
55809      * return false or null to disable editing.
55810      * @param {Number} colIndex The column index
55811      * @param {Number} rowIndex The row index
55812      * @return {Object}
55813      */
55814     getCellEditor : function(colIndex, rowIndex){
55815         return this.config[colIndex].editor;
55816     },
55817
55818     /**
55819      * Sets if a column is editable.
55820      * @param {Number} col The column index
55821      * @param {Boolean} editable True if the column is editable
55822      */
55823     setEditable : function(col, editable){
55824         this.config[col].editable = editable;
55825     },
55826
55827
55828     /**
55829      * Returns true if the column is hidden.
55830      * @param {Number} colIndex The column index
55831      * @return {Boolean}
55832      */
55833     isHidden : function(colIndex){
55834         return this.config[colIndex].hidden;
55835     },
55836
55837
55838     /**
55839      * Returns true if the column width cannot be changed
55840      */
55841     isFixed : function(colIndex){
55842         return this.config[colIndex].fixed;
55843     },
55844
55845     /**
55846      * Returns true if the column can be resized
55847      * @return {Boolean}
55848      */
55849     isResizable : function(colIndex){
55850         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55851     },
55852     /**
55853      * Sets if a column is hidden.
55854      * @param {Number} colIndex The column index
55855      * @param {Boolean} hidden True if the column is hidden
55856      */
55857     setHidden : function(colIndex, hidden){
55858         this.config[colIndex].hidden = hidden;
55859         this.totalWidth = null;
55860         this.fireEvent("hiddenchange", this, colIndex, hidden);
55861     },
55862
55863     /**
55864      * Sets the editor for a column.
55865      * @param {Number} col The column index
55866      * @param {Object} editor The editor object
55867      */
55868     setEditor : function(col, editor){
55869         this.config[col].editor = editor;
55870     }
55871 });
55872
55873 Roo.grid.ColumnModel.defaultRenderer = function(value){
55874         if(typeof value == "string" && value.length < 1){
55875             return "&#160;";
55876         }
55877         return value;
55878 };
55879
55880 // Alias for backwards compatibility
55881 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55882 /*
55883  * Based on:
55884  * Ext JS Library 1.1.1
55885  * Copyright(c) 2006-2007, Ext JS, LLC.
55886  *
55887  * Originally Released Under LGPL - original licence link has changed is not relivant.
55888  *
55889  * Fork - LGPL
55890  * <script type="text/javascript">
55891  */
55892
55893 /**
55894  * @class Roo.grid.AbstractSelectionModel
55895  * @extends Roo.util.Observable
55896  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55897  * implemented by descendant classes.  This class should not be directly instantiated.
55898  * @constructor
55899  */
55900 Roo.grid.AbstractSelectionModel = function(){
55901     this.locked = false;
55902     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55903 };
55904
55905 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55906     /** @ignore Called by the grid automatically. Do not call directly. */
55907     init : function(grid){
55908         this.grid = grid;
55909         this.initEvents();
55910     },
55911
55912     /**
55913      * Locks the selections.
55914      */
55915     lock : function(){
55916         this.locked = true;
55917     },
55918
55919     /**
55920      * Unlocks the selections.
55921      */
55922     unlock : function(){
55923         this.locked = false;
55924     },
55925
55926     /**
55927      * Returns true if the selections are locked.
55928      * @return {Boolean}
55929      */
55930     isLocked : function(){
55931         return this.locked;
55932     }
55933 });/*
55934  * Based on:
55935  * Ext JS Library 1.1.1
55936  * Copyright(c) 2006-2007, Ext JS, LLC.
55937  *
55938  * Originally Released Under LGPL - original licence link has changed is not relivant.
55939  *
55940  * Fork - LGPL
55941  * <script type="text/javascript">
55942  */
55943 /**
55944  * @extends Roo.grid.AbstractSelectionModel
55945  * @class Roo.grid.RowSelectionModel
55946  * The default SelectionModel used by {@link Roo.grid.Grid}.
55947  * It supports multiple selections and keyboard selection/navigation. 
55948  * @constructor
55949  * @param {Object} config
55950  */
55951 Roo.grid.RowSelectionModel = function(config){
55952     Roo.apply(this, config);
55953     this.selections = new Roo.util.MixedCollection(false, function(o){
55954         return o.id;
55955     });
55956
55957     this.last = false;
55958     this.lastActive = false;
55959
55960     this.addEvents({
55961         /**
55962              * @event selectionchange
55963              * Fires when the selection changes
55964              * @param {SelectionModel} this
55965              */
55966             "selectionchange" : true,
55967         /**
55968              * @event afterselectionchange
55969              * Fires after the selection changes (eg. by key press or clicking)
55970              * @param {SelectionModel} this
55971              */
55972             "afterselectionchange" : true,
55973         /**
55974              * @event beforerowselect
55975              * Fires when a row is selected being selected, return false to cancel.
55976              * @param {SelectionModel} this
55977              * @param {Number} rowIndex The selected index
55978              * @param {Boolean} keepExisting False if other selections will be cleared
55979              */
55980             "beforerowselect" : true,
55981         /**
55982              * @event rowselect
55983              * Fires when a row is selected.
55984              * @param {SelectionModel} this
55985              * @param {Number} rowIndex The selected index
55986              * @param {Roo.data.Record} r The record
55987              */
55988             "rowselect" : true,
55989         /**
55990              * @event rowdeselect
55991              * Fires when a row is deselected.
55992              * @param {SelectionModel} this
55993              * @param {Number} rowIndex The selected index
55994              */
55995         "rowdeselect" : true
55996     });
55997     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55998     this.locked = false;
55999 };
56000
56001 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
56002     /**
56003      * @cfg {Boolean} singleSelect
56004      * True to allow selection of only one row at a time (defaults to false)
56005      */
56006     singleSelect : false,
56007
56008     // private
56009     initEvents : function(){
56010
56011         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
56012             this.grid.on("mousedown", this.handleMouseDown, this);
56013         }else{ // allow click to work like normal
56014             this.grid.on("rowclick", this.handleDragableRowClick, this);
56015         }
56016
56017         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
56018             "up" : function(e){
56019                 if(!e.shiftKey){
56020                     this.selectPrevious(e.shiftKey);
56021                 }else if(this.last !== false && this.lastActive !== false){
56022                     var last = this.last;
56023                     this.selectRange(this.last,  this.lastActive-1);
56024                     this.grid.getView().focusRow(this.lastActive);
56025                     if(last !== false){
56026                         this.last = last;
56027                     }
56028                 }else{
56029                     this.selectFirstRow();
56030                 }
56031                 this.fireEvent("afterselectionchange", this);
56032             },
56033             "down" : function(e){
56034                 if(!e.shiftKey){
56035                     this.selectNext(e.shiftKey);
56036                 }else if(this.last !== false && this.lastActive !== false){
56037                     var last = this.last;
56038                     this.selectRange(this.last,  this.lastActive+1);
56039                     this.grid.getView().focusRow(this.lastActive);
56040                     if(last !== false){
56041                         this.last = last;
56042                     }
56043                 }else{
56044                     this.selectFirstRow();
56045                 }
56046                 this.fireEvent("afterselectionchange", this);
56047             },
56048             scope: this
56049         });
56050
56051         var view = this.grid.view;
56052         view.on("refresh", this.onRefresh, this);
56053         view.on("rowupdated", this.onRowUpdated, this);
56054         view.on("rowremoved", this.onRemove, this);
56055     },
56056
56057     // private
56058     onRefresh : function(){
56059         var ds = this.grid.dataSource, i, v = this.grid.view;
56060         var s = this.selections;
56061         s.each(function(r){
56062             if((i = ds.indexOfId(r.id)) != -1){
56063                 v.onRowSelect(i);
56064                 s.add(ds.getAt(i)); // updating the selection relate data
56065             }else{
56066                 s.remove(r);
56067             }
56068         });
56069     },
56070
56071     // private
56072     onRemove : function(v, index, r){
56073         this.selections.remove(r);
56074     },
56075
56076     // private
56077     onRowUpdated : function(v, index, r){
56078         if(this.isSelected(r)){
56079             v.onRowSelect(index);
56080         }
56081     },
56082
56083     /**
56084      * Select records.
56085      * @param {Array} records The records to select
56086      * @param {Boolean} keepExisting (optional) True to keep existing selections
56087      */
56088     selectRecords : function(records, keepExisting){
56089         if(!keepExisting){
56090             this.clearSelections();
56091         }
56092         var ds = this.grid.dataSource;
56093         for(var i = 0, len = records.length; i < len; i++){
56094             this.selectRow(ds.indexOf(records[i]), true);
56095         }
56096     },
56097
56098     /**
56099      * Gets the number of selected rows.
56100      * @return {Number}
56101      */
56102     getCount : function(){
56103         return this.selections.length;
56104     },
56105
56106     /**
56107      * Selects the first row in the grid.
56108      */
56109     selectFirstRow : function(){
56110         this.selectRow(0);
56111     },
56112
56113     /**
56114      * Select the last row.
56115      * @param {Boolean} keepExisting (optional) True to keep existing selections
56116      */
56117     selectLastRow : function(keepExisting){
56118         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
56119     },
56120
56121     /**
56122      * Selects the row immediately following the last selected row.
56123      * @param {Boolean} keepExisting (optional) True to keep existing selections
56124      */
56125     selectNext : function(keepExisting){
56126         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
56127             this.selectRow(this.last+1, keepExisting);
56128             this.grid.getView().focusRow(this.last);
56129         }
56130     },
56131
56132     /**
56133      * Selects the row that precedes the last selected row.
56134      * @param {Boolean} keepExisting (optional) True to keep existing selections
56135      */
56136     selectPrevious : function(keepExisting){
56137         if(this.last){
56138             this.selectRow(this.last-1, keepExisting);
56139             this.grid.getView().focusRow(this.last);
56140         }
56141     },
56142
56143     /**
56144      * Returns the selected records
56145      * @return {Array} Array of selected records
56146      */
56147     getSelections : function(){
56148         return [].concat(this.selections.items);
56149     },
56150
56151     /**
56152      * Returns the first selected record.
56153      * @return {Record}
56154      */
56155     getSelected : function(){
56156         return this.selections.itemAt(0);
56157     },
56158
56159
56160     /**
56161      * Clears all selections.
56162      */
56163     clearSelections : function(fast){
56164         if(this.locked) {
56165             return;
56166         }
56167         if(fast !== true){
56168             var ds = this.grid.dataSource;
56169             var s = this.selections;
56170             s.each(function(r){
56171                 this.deselectRow(ds.indexOfId(r.id));
56172             }, this);
56173             s.clear();
56174         }else{
56175             this.selections.clear();
56176         }
56177         this.last = false;
56178     },
56179
56180
56181     /**
56182      * Selects all rows.
56183      */
56184     selectAll : function(){
56185         if(this.locked) {
56186             return;
56187         }
56188         this.selections.clear();
56189         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56190             this.selectRow(i, true);
56191         }
56192     },
56193
56194     /**
56195      * Returns True if there is a selection.
56196      * @return {Boolean}
56197      */
56198     hasSelection : function(){
56199         return this.selections.length > 0;
56200     },
56201
56202     /**
56203      * Returns True if the specified row is selected.
56204      * @param {Number/Record} record The record or index of the record to check
56205      * @return {Boolean}
56206      */
56207     isSelected : function(index){
56208         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56209         return (r && this.selections.key(r.id) ? true : false);
56210     },
56211
56212     /**
56213      * Returns True if the specified record id is selected.
56214      * @param {String} id The id of record to check
56215      * @return {Boolean}
56216      */
56217     isIdSelected : function(id){
56218         return (this.selections.key(id) ? true : false);
56219     },
56220
56221     // private
56222     handleMouseDown : function(e, t){
56223         var view = this.grid.getView(), rowIndex;
56224         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56225             return;
56226         };
56227         if(e.shiftKey && this.last !== false){
56228             var last = this.last;
56229             this.selectRange(last, rowIndex, e.ctrlKey);
56230             this.last = last; // reset the last
56231             view.focusRow(rowIndex);
56232         }else{
56233             var isSelected = this.isSelected(rowIndex);
56234             if(e.button !== 0 && isSelected){
56235                 view.focusRow(rowIndex);
56236             }else if(e.ctrlKey && isSelected){
56237                 this.deselectRow(rowIndex);
56238             }else if(!isSelected){
56239                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56240                 view.focusRow(rowIndex);
56241             }
56242         }
56243         this.fireEvent("afterselectionchange", this);
56244     },
56245     // private
56246     handleDragableRowClick :  function(grid, rowIndex, e) 
56247     {
56248         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56249             this.selectRow(rowIndex, false);
56250             grid.view.focusRow(rowIndex);
56251              this.fireEvent("afterselectionchange", this);
56252         }
56253     },
56254     
56255     /**
56256      * Selects multiple rows.
56257      * @param {Array} rows Array of the indexes of the row to select
56258      * @param {Boolean} keepExisting (optional) True to keep existing selections
56259      */
56260     selectRows : function(rows, keepExisting){
56261         if(!keepExisting){
56262             this.clearSelections();
56263         }
56264         for(var i = 0, len = rows.length; i < len; i++){
56265             this.selectRow(rows[i], true);
56266         }
56267     },
56268
56269     /**
56270      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56271      * @param {Number} startRow The index of the first row in the range
56272      * @param {Number} endRow The index of the last row in the range
56273      * @param {Boolean} keepExisting (optional) True to retain existing selections
56274      */
56275     selectRange : function(startRow, endRow, keepExisting){
56276         if(this.locked) {
56277             return;
56278         }
56279         if(!keepExisting){
56280             this.clearSelections();
56281         }
56282         if(startRow <= endRow){
56283             for(var i = startRow; i <= endRow; i++){
56284                 this.selectRow(i, true);
56285             }
56286         }else{
56287             for(var i = startRow; i >= endRow; i--){
56288                 this.selectRow(i, true);
56289             }
56290         }
56291     },
56292
56293     /**
56294      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56295      * @param {Number} startRow The index of the first row in the range
56296      * @param {Number} endRow The index of the last row in the range
56297      */
56298     deselectRange : function(startRow, endRow, preventViewNotify){
56299         if(this.locked) {
56300             return;
56301         }
56302         for(var i = startRow; i <= endRow; i++){
56303             this.deselectRow(i, preventViewNotify);
56304         }
56305     },
56306
56307     /**
56308      * Selects a row.
56309      * @param {Number} row The index of the row to select
56310      * @param {Boolean} keepExisting (optional) True to keep existing selections
56311      */
56312     selectRow : function(index, keepExisting, preventViewNotify){
56313         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
56314             return;
56315         }
56316         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56317             if(!keepExisting || this.singleSelect){
56318                 this.clearSelections();
56319             }
56320             var r = this.grid.dataSource.getAt(index);
56321             this.selections.add(r);
56322             this.last = this.lastActive = index;
56323             if(!preventViewNotify){
56324                 this.grid.getView().onRowSelect(index);
56325             }
56326             this.fireEvent("rowselect", this, index, r);
56327             this.fireEvent("selectionchange", this);
56328         }
56329     },
56330
56331     /**
56332      * Deselects a row.
56333      * @param {Number} row The index of the row to deselect
56334      */
56335     deselectRow : function(index, preventViewNotify){
56336         if(this.locked) {
56337             return;
56338         }
56339         if(this.last == index){
56340             this.last = false;
56341         }
56342         if(this.lastActive == index){
56343             this.lastActive = false;
56344         }
56345         var r = this.grid.dataSource.getAt(index);
56346         this.selections.remove(r);
56347         if(!preventViewNotify){
56348             this.grid.getView().onRowDeselect(index);
56349         }
56350         this.fireEvent("rowdeselect", this, index);
56351         this.fireEvent("selectionchange", this);
56352     },
56353
56354     // private
56355     restoreLast : function(){
56356         if(this._last){
56357             this.last = this._last;
56358         }
56359     },
56360
56361     // private
56362     acceptsNav : function(row, col, cm){
56363         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56364     },
56365
56366     // private
56367     onEditorKey : function(field, e){
56368         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56369         if(k == e.TAB){
56370             e.stopEvent();
56371             ed.completeEdit();
56372             if(e.shiftKey){
56373                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56374             }else{
56375                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56376             }
56377         }else if(k == e.ENTER && !e.ctrlKey){
56378             e.stopEvent();
56379             ed.completeEdit();
56380             if(e.shiftKey){
56381                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56382             }else{
56383                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56384             }
56385         }else if(k == e.ESC){
56386             ed.cancelEdit();
56387         }
56388         if(newCell){
56389             g.startEditing(newCell[0], newCell[1]);
56390         }
56391     }
56392 });/*
56393  * Based on:
56394  * Ext JS Library 1.1.1
56395  * Copyright(c) 2006-2007, Ext JS, LLC.
56396  *
56397  * Originally Released Under LGPL - original licence link has changed is not relivant.
56398  *
56399  * Fork - LGPL
56400  * <script type="text/javascript">
56401  */
56402 /**
56403  * @class Roo.grid.CellSelectionModel
56404  * @extends Roo.grid.AbstractSelectionModel
56405  * This class provides the basic implementation for cell selection in a grid.
56406  * @constructor
56407  * @param {Object} config The object containing the configuration of this model.
56408  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56409  */
56410 Roo.grid.CellSelectionModel = function(config){
56411     Roo.apply(this, config);
56412
56413     this.selection = null;
56414
56415     this.addEvents({
56416         /**
56417              * @event beforerowselect
56418              * Fires before a cell is selected.
56419              * @param {SelectionModel} this
56420              * @param {Number} rowIndex The selected row index
56421              * @param {Number} colIndex The selected cell index
56422              */
56423             "beforecellselect" : true,
56424         /**
56425              * @event cellselect
56426              * Fires when a cell is selected.
56427              * @param {SelectionModel} this
56428              * @param {Number} rowIndex The selected row index
56429              * @param {Number} colIndex The selected cell index
56430              */
56431             "cellselect" : true,
56432         /**
56433              * @event selectionchange
56434              * Fires when the active selection changes.
56435              * @param {SelectionModel} this
56436              * @param {Object} selection null for no selection or an object (o) with two properties
56437                 <ul>
56438                 <li>o.record: the record object for the row the selection is in</li>
56439                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56440                 </ul>
56441              */
56442             "selectionchange" : true,
56443         /**
56444              * @event tabend
56445              * Fires when the tab (or enter) was pressed on the last editable cell
56446              * You can use this to trigger add new row.
56447              * @param {SelectionModel} this
56448              */
56449             "tabend" : true,
56450          /**
56451              * @event beforeeditnext
56452              * Fires before the next editable sell is made active
56453              * You can use this to skip to another cell or fire the tabend
56454              *    if you set cell to false
56455              * @param {Object} eventdata object : { cell : [ row, col ] } 
56456              */
56457             "beforeeditnext" : true
56458     });
56459     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56460 };
56461
56462 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56463     
56464     enter_is_tab: false,
56465
56466     /** @ignore */
56467     initEvents : function(){
56468         this.grid.on("mousedown", this.handleMouseDown, this);
56469         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56470         var view = this.grid.view;
56471         view.on("refresh", this.onViewChange, this);
56472         view.on("rowupdated", this.onRowUpdated, this);
56473         view.on("beforerowremoved", this.clearSelections, this);
56474         view.on("beforerowsinserted", this.clearSelections, this);
56475         if(this.grid.isEditor){
56476             this.grid.on("beforeedit", this.beforeEdit,  this);
56477         }
56478     },
56479
56480         //private
56481     beforeEdit : function(e){
56482         this.select(e.row, e.column, false, true, e.record);
56483     },
56484
56485         //private
56486     onRowUpdated : function(v, index, r){
56487         if(this.selection && this.selection.record == r){
56488             v.onCellSelect(index, this.selection.cell[1]);
56489         }
56490     },
56491
56492         //private
56493     onViewChange : function(){
56494         this.clearSelections(true);
56495     },
56496
56497         /**
56498          * Returns the currently selected cell,.
56499          * @return {Array} The selected cell (row, column) or null if none selected.
56500          */
56501     getSelectedCell : function(){
56502         return this.selection ? this.selection.cell : null;
56503     },
56504
56505     /**
56506      * Clears all selections.
56507      * @param {Boolean} true to prevent the gridview from being notified about the change.
56508      */
56509     clearSelections : function(preventNotify){
56510         var s = this.selection;
56511         if(s){
56512             if(preventNotify !== true){
56513                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56514             }
56515             this.selection = null;
56516             this.fireEvent("selectionchange", this, null);
56517         }
56518     },
56519
56520     /**
56521      * Returns true if there is a selection.
56522      * @return {Boolean}
56523      */
56524     hasSelection : function(){
56525         return this.selection ? true : false;
56526     },
56527
56528     /** @ignore */
56529     handleMouseDown : function(e, t){
56530         var v = this.grid.getView();
56531         if(this.isLocked()){
56532             return;
56533         };
56534         var row = v.findRowIndex(t);
56535         var cell = v.findCellIndex(t);
56536         if(row !== false && cell !== false){
56537             this.select(row, cell);
56538         }
56539     },
56540
56541     /**
56542      * Selects a cell.
56543      * @param {Number} rowIndex
56544      * @param {Number} collIndex
56545      */
56546     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56547         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56548             this.clearSelections();
56549             r = r || this.grid.dataSource.getAt(rowIndex);
56550             this.selection = {
56551                 record : r,
56552                 cell : [rowIndex, colIndex]
56553             };
56554             if(!preventViewNotify){
56555                 var v = this.grid.getView();
56556                 v.onCellSelect(rowIndex, colIndex);
56557                 if(preventFocus !== true){
56558                     v.focusCell(rowIndex, colIndex);
56559                 }
56560             }
56561             this.fireEvent("cellselect", this, rowIndex, colIndex);
56562             this.fireEvent("selectionchange", this, this.selection);
56563         }
56564     },
56565
56566         //private
56567     isSelectable : function(rowIndex, colIndex, cm){
56568         return !cm.isHidden(colIndex);
56569     },
56570
56571     /** @ignore */
56572     handleKeyDown : function(e){
56573         //Roo.log('Cell Sel Model handleKeyDown');
56574         if(!e.isNavKeyPress()){
56575             return;
56576         }
56577         var g = this.grid, s = this.selection;
56578         if(!s){
56579             e.stopEvent();
56580             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56581             if(cell){
56582                 this.select(cell[0], cell[1]);
56583             }
56584             return;
56585         }
56586         var sm = this;
56587         var walk = function(row, col, step){
56588             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56589         };
56590         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56591         var newCell;
56592
56593       
56594
56595         switch(k){
56596             case e.TAB:
56597                 // handled by onEditorKey
56598                 if (g.isEditor && g.editing) {
56599                     return;
56600                 }
56601                 if(e.shiftKey) {
56602                     newCell = walk(r, c-1, -1);
56603                 } else {
56604                     newCell = walk(r, c+1, 1);
56605                 }
56606                 break;
56607             
56608             case e.DOWN:
56609                newCell = walk(r+1, c, 1);
56610                 break;
56611             
56612             case e.UP:
56613                 newCell = walk(r-1, c, -1);
56614                 break;
56615             
56616             case e.RIGHT:
56617                 newCell = walk(r, c+1, 1);
56618                 break;
56619             
56620             case e.LEFT:
56621                 newCell = walk(r, c-1, -1);
56622                 break;
56623             
56624             case e.ENTER:
56625                 
56626                 if(g.isEditor && !g.editing){
56627                    g.startEditing(r, c);
56628                    e.stopEvent();
56629                    return;
56630                 }
56631                 
56632                 
56633              break;
56634         };
56635         if(newCell){
56636             this.select(newCell[0], newCell[1]);
56637             e.stopEvent();
56638             
56639         }
56640     },
56641
56642     acceptsNav : function(row, col, cm){
56643         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56644     },
56645     /**
56646      * Selects a cell.
56647      * @param {Number} field (not used) - as it's normally used as a listener
56648      * @param {Number} e - event - fake it by using
56649      *
56650      * var e = Roo.EventObjectImpl.prototype;
56651      * e.keyCode = e.TAB
56652      *
56653      * 
56654      */
56655     onEditorKey : function(field, e){
56656         
56657         var k = e.getKey(),
56658             newCell,
56659             g = this.grid,
56660             ed = g.activeEditor,
56661             forward = false;
56662         ///Roo.log('onEditorKey' + k);
56663         
56664         
56665         if (this.enter_is_tab && k == e.ENTER) {
56666             k = e.TAB;
56667         }
56668         
56669         if(k == e.TAB){
56670             if(e.shiftKey){
56671                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56672             }else{
56673                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56674                 forward = true;
56675             }
56676             
56677             e.stopEvent();
56678             
56679         } else if(k == e.ENTER &&  !e.ctrlKey){
56680             ed.completeEdit();
56681             e.stopEvent();
56682             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56683         
56684                 } else if(k == e.ESC){
56685             ed.cancelEdit();
56686         }
56687                 
56688         if (newCell) {
56689             var ecall = { cell : newCell, forward : forward };
56690             this.fireEvent('beforeeditnext', ecall );
56691             newCell = ecall.cell;
56692                         forward = ecall.forward;
56693         }
56694                 
56695         if(newCell){
56696             //Roo.log('next cell after edit');
56697             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56698         } else if (forward) {
56699             // tabbed past last
56700             this.fireEvent.defer(100, this, ['tabend',this]);
56701         }
56702     }
56703 });/*
56704  * Based on:
56705  * Ext JS Library 1.1.1
56706  * Copyright(c) 2006-2007, Ext JS, LLC.
56707  *
56708  * Originally Released Under LGPL - original licence link has changed is not relivant.
56709  *
56710  * Fork - LGPL
56711  * <script type="text/javascript">
56712  */
56713  
56714 /**
56715  * @class Roo.grid.EditorGrid
56716  * @extends Roo.grid.Grid
56717  * Class for creating and editable grid.
56718  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56719  * The container MUST have some type of size defined for the grid to fill. The container will be 
56720  * automatically set to position relative if it isn't already.
56721  * @param {Object} dataSource The data model to bind to
56722  * @param {Object} colModel The column model with info about this grid's columns
56723  */
56724 Roo.grid.EditorGrid = function(container, config){
56725     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56726     this.getGridEl().addClass("xedit-grid");
56727
56728     if(!this.selModel){
56729         this.selModel = new Roo.grid.CellSelectionModel();
56730     }
56731
56732     this.activeEditor = null;
56733
56734         this.addEvents({
56735             /**
56736              * @event beforeedit
56737              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56738              * <ul style="padding:5px;padding-left:16px;">
56739              * <li>grid - This grid</li>
56740              * <li>record - The record being edited</li>
56741              * <li>field - The field name being edited</li>
56742              * <li>value - The value for the field being edited.</li>
56743              * <li>row - The grid row index</li>
56744              * <li>column - The grid column index</li>
56745              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56746              * </ul>
56747              * @param {Object} e An edit event (see above for description)
56748              */
56749             "beforeedit" : true,
56750             /**
56751              * @event afteredit
56752              * Fires after a cell is edited. <br />
56753              * <ul style="padding:5px;padding-left:16px;">
56754              * <li>grid - This grid</li>
56755              * <li>record - The record being edited</li>
56756              * <li>field - The field name being edited</li>
56757              * <li>value - The value being set</li>
56758              * <li>originalValue - The original value for the field, before the edit.</li>
56759              * <li>row - The grid row index</li>
56760              * <li>column - The grid column index</li>
56761              * </ul>
56762              * @param {Object} e An edit event (see above for description)
56763              */
56764             "afteredit" : true,
56765             /**
56766              * @event validateedit
56767              * Fires after a cell is edited, but before the value is set in the record. 
56768          * You can use this to modify the value being set in the field, Return false
56769              * to cancel the change. The edit event object has the following properties <br />
56770              * <ul style="padding:5px;padding-left:16px;">
56771          * <li>editor - This editor</li>
56772              * <li>grid - This grid</li>
56773              * <li>record - The record being edited</li>
56774              * <li>field - The field name being edited</li>
56775              * <li>value - The value being set</li>
56776              * <li>originalValue - The original value for the field, before the edit.</li>
56777              * <li>row - The grid row index</li>
56778              * <li>column - The grid column index</li>
56779              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56780              * </ul>
56781              * @param {Object} e An edit event (see above for description)
56782              */
56783             "validateedit" : true
56784         });
56785     this.on("bodyscroll", this.stopEditing,  this);
56786     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56787 };
56788
56789 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56790     /**
56791      * @cfg {Number} clicksToEdit
56792      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56793      */
56794     clicksToEdit: 2,
56795
56796     // private
56797     isEditor : true,
56798     // private
56799     trackMouseOver: false, // causes very odd FF errors
56800
56801     onCellDblClick : function(g, row, col){
56802         this.startEditing(row, col);
56803     },
56804
56805     onEditComplete : function(ed, value, startValue){
56806         this.editing = false;
56807         this.activeEditor = null;
56808         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56809         var r = ed.record;
56810         var field = this.colModel.getDataIndex(ed.col);
56811         var e = {
56812             grid: this,
56813             record: r,
56814             field: field,
56815             originalValue: startValue,
56816             value: value,
56817             row: ed.row,
56818             column: ed.col,
56819             cancel:false,
56820             editor: ed
56821         };
56822         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
56823         cell.show();
56824           
56825         if(String(value) !== String(startValue)){
56826             
56827             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56828                 r.set(field, e.value);
56829                 // if we are dealing with a combo box..
56830                 // then we also set the 'name' colum to be the displayField
56831                 if (ed.field.displayField && ed.field.name) {
56832                     r.set(ed.field.name, ed.field.el.dom.value);
56833                 }
56834                 
56835                 delete e.cancel; //?? why!!!
56836                 this.fireEvent("afteredit", e);
56837             }
56838         } else {
56839             this.fireEvent("afteredit", e); // always fire it!
56840         }
56841         this.view.focusCell(ed.row, ed.col);
56842     },
56843
56844     /**
56845      * Starts editing the specified for the specified row/column
56846      * @param {Number} rowIndex
56847      * @param {Number} colIndex
56848      */
56849     startEditing : function(row, col){
56850         this.stopEditing();
56851         if(this.colModel.isCellEditable(col, row)){
56852             this.view.ensureVisible(row, col, true);
56853           
56854             var r = this.dataSource.getAt(row);
56855             var field = this.colModel.getDataIndex(col);
56856             var cell = Roo.get(this.view.getCell(row,col));
56857             var e = {
56858                 grid: this,
56859                 record: r,
56860                 field: field,
56861                 value: r.data[field],
56862                 row: row,
56863                 column: col,
56864                 cancel:false 
56865             };
56866             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56867                 this.editing = true;
56868                 var ed = this.colModel.getCellEditor(col, row);
56869                 
56870                 if (!ed) {
56871                     return;
56872                 }
56873                 if(!ed.rendered){
56874                     ed.render(ed.parentEl || document.body);
56875                 }
56876                 ed.field.reset();
56877                
56878                 cell.hide();
56879                 
56880                 (function(){ // complex but required for focus issues in safari, ie and opera
56881                     ed.row = row;
56882                     ed.col = col;
56883                     ed.record = r;
56884                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56885                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56886                     this.activeEditor = ed;
56887                     var v = r.data[field];
56888                     ed.startEdit(this.view.getCell(row, col), v);
56889                     // combo's with 'displayField and name set
56890                     if (ed.field.displayField && ed.field.name) {
56891                         ed.field.el.dom.value = r.data[ed.field.name];
56892                     }
56893                     
56894                     
56895                 }).defer(50, this);
56896             }
56897         }
56898     },
56899         
56900     /**
56901      * Stops any active editing
56902      */
56903     stopEditing : function(){
56904         if(this.activeEditor){
56905             this.activeEditor.completeEdit();
56906         }
56907         this.activeEditor = null;
56908     },
56909         
56910          /**
56911      * Called to get grid's drag proxy text, by default returns this.ddText.
56912      * @return {String}
56913      */
56914     getDragDropText : function(){
56915         var count = this.selModel.getSelectedCell() ? 1 : 0;
56916         return String.format(this.ddText, count, count == 1 ? '' : 's');
56917     }
56918         
56919 });/*
56920  * Based on:
56921  * Ext JS Library 1.1.1
56922  * Copyright(c) 2006-2007, Ext JS, LLC.
56923  *
56924  * Originally Released Under LGPL - original licence link has changed is not relivant.
56925  *
56926  * Fork - LGPL
56927  * <script type="text/javascript">
56928  */
56929
56930 // private - not really -- you end up using it !
56931 // This is a support class used internally by the Grid components
56932
56933 /**
56934  * @class Roo.grid.GridEditor
56935  * @extends Roo.Editor
56936  * Class for creating and editable grid elements.
56937  * @param {Object} config any settings (must include field)
56938  */
56939 Roo.grid.GridEditor = function(field, config){
56940     if (!config && field.field) {
56941         config = field;
56942         field = Roo.factory(config.field, Roo.form);
56943     }
56944     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56945     field.monitorTab = false;
56946 };
56947
56948 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56949     
56950     /**
56951      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56952      */
56953     
56954     alignment: "tl-tl",
56955     autoSize: "width",
56956     hideEl : false,
56957     cls: "x-small-editor x-grid-editor",
56958     shim:false,
56959     shadow:"frame"
56960 });/*
56961  * Based on:
56962  * Ext JS Library 1.1.1
56963  * Copyright(c) 2006-2007, Ext JS, LLC.
56964  *
56965  * Originally Released Under LGPL - original licence link has changed is not relivant.
56966  *
56967  * Fork - LGPL
56968  * <script type="text/javascript">
56969  */
56970   
56971
56972   
56973 Roo.grid.PropertyRecord = Roo.data.Record.create([
56974     {name:'name',type:'string'},  'value'
56975 ]);
56976
56977
56978 Roo.grid.PropertyStore = function(grid, source){
56979     this.grid = grid;
56980     this.store = new Roo.data.Store({
56981         recordType : Roo.grid.PropertyRecord
56982     });
56983     this.store.on('update', this.onUpdate,  this);
56984     if(source){
56985         this.setSource(source);
56986     }
56987     Roo.grid.PropertyStore.superclass.constructor.call(this);
56988 };
56989
56990
56991
56992 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56993     setSource : function(o){
56994         this.source = o;
56995         this.store.removeAll();
56996         var data = [];
56997         for(var k in o){
56998             if(this.isEditableValue(o[k])){
56999                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
57000             }
57001         }
57002         this.store.loadRecords({records: data}, {}, true);
57003     },
57004
57005     onUpdate : function(ds, record, type){
57006         if(type == Roo.data.Record.EDIT){
57007             var v = record.data['value'];
57008             var oldValue = record.modified['value'];
57009             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
57010                 this.source[record.id] = v;
57011                 record.commit();
57012                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
57013             }else{
57014                 record.reject();
57015             }
57016         }
57017     },
57018
57019     getProperty : function(row){
57020        return this.store.getAt(row);
57021     },
57022
57023     isEditableValue: function(val){
57024         if(val && val instanceof Date){
57025             return true;
57026         }else if(typeof val == 'object' || typeof val == 'function'){
57027             return false;
57028         }
57029         return true;
57030     },
57031
57032     setValue : function(prop, value){
57033         this.source[prop] = value;
57034         this.store.getById(prop).set('value', value);
57035     },
57036
57037     getSource : function(){
57038         return this.source;
57039     }
57040 });
57041
57042 Roo.grid.PropertyColumnModel = function(grid, store){
57043     this.grid = grid;
57044     var g = Roo.grid;
57045     g.PropertyColumnModel.superclass.constructor.call(this, [
57046         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
57047         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
57048     ]);
57049     this.store = store;
57050     this.bselect = Roo.DomHelper.append(document.body, {
57051         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
57052             {tag: 'option', value: 'true', html: 'true'},
57053             {tag: 'option', value: 'false', html: 'false'}
57054         ]
57055     });
57056     Roo.id(this.bselect);
57057     var f = Roo.form;
57058     this.editors = {
57059         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
57060         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
57061         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
57062         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
57063         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
57064     };
57065     this.renderCellDelegate = this.renderCell.createDelegate(this);
57066     this.renderPropDelegate = this.renderProp.createDelegate(this);
57067 };
57068
57069 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
57070     
57071     
57072     nameText : 'Name',
57073     valueText : 'Value',
57074     
57075     dateFormat : 'm/j/Y',
57076     
57077     
57078     renderDate : function(dateVal){
57079         return dateVal.dateFormat(this.dateFormat);
57080     },
57081
57082     renderBool : function(bVal){
57083         return bVal ? 'true' : 'false';
57084     },
57085
57086     isCellEditable : function(colIndex, rowIndex){
57087         return colIndex == 1;
57088     },
57089
57090     getRenderer : function(col){
57091         return col == 1 ?
57092             this.renderCellDelegate : this.renderPropDelegate;
57093     },
57094
57095     renderProp : function(v){
57096         return this.getPropertyName(v);
57097     },
57098
57099     renderCell : function(val){
57100         var rv = val;
57101         if(val instanceof Date){
57102             rv = this.renderDate(val);
57103         }else if(typeof val == 'boolean'){
57104             rv = this.renderBool(val);
57105         }
57106         return Roo.util.Format.htmlEncode(rv);
57107     },
57108
57109     getPropertyName : function(name){
57110         var pn = this.grid.propertyNames;
57111         return pn && pn[name] ? pn[name] : name;
57112     },
57113
57114     getCellEditor : function(colIndex, rowIndex){
57115         var p = this.store.getProperty(rowIndex);
57116         var n = p.data['name'], val = p.data['value'];
57117         
57118         if(typeof(this.grid.customEditors[n]) == 'string'){
57119             return this.editors[this.grid.customEditors[n]];
57120         }
57121         if(typeof(this.grid.customEditors[n]) != 'undefined'){
57122             return this.grid.customEditors[n];
57123         }
57124         if(val instanceof Date){
57125             return this.editors['date'];
57126         }else if(typeof val == 'number'){
57127             return this.editors['number'];
57128         }else if(typeof val == 'boolean'){
57129             return this.editors['boolean'];
57130         }else{
57131             return this.editors['string'];
57132         }
57133     }
57134 });
57135
57136 /**
57137  * @class Roo.grid.PropertyGrid
57138  * @extends Roo.grid.EditorGrid
57139  * This class represents the  interface of a component based property grid control.
57140  * <br><br>Usage:<pre><code>
57141  var grid = new Roo.grid.PropertyGrid("my-container-id", {
57142       
57143  });
57144  // set any options
57145  grid.render();
57146  * </code></pre>
57147   
57148  * @constructor
57149  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57150  * The container MUST have some type of size defined for the grid to fill. The container will be
57151  * automatically set to position relative if it isn't already.
57152  * @param {Object} config A config object that sets properties on this grid.
57153  */
57154 Roo.grid.PropertyGrid = function(container, config){
57155     config = config || {};
57156     var store = new Roo.grid.PropertyStore(this);
57157     this.store = store;
57158     var cm = new Roo.grid.PropertyColumnModel(this, store);
57159     store.store.sort('name', 'ASC');
57160     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57161         ds: store.store,
57162         cm: cm,
57163         enableColLock:false,
57164         enableColumnMove:false,
57165         stripeRows:false,
57166         trackMouseOver: false,
57167         clicksToEdit:1
57168     }, config));
57169     this.getGridEl().addClass('x-props-grid');
57170     this.lastEditRow = null;
57171     this.on('columnresize', this.onColumnResize, this);
57172     this.addEvents({
57173          /**
57174              * @event beforepropertychange
57175              * Fires before a property changes (return false to stop?)
57176              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57177              * @param {String} id Record Id
57178              * @param {String} newval New Value
57179          * @param {String} oldval Old Value
57180              */
57181         "beforepropertychange": true,
57182         /**
57183              * @event propertychange
57184              * Fires after a property changes
57185              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57186              * @param {String} id Record Id
57187              * @param {String} newval New Value
57188          * @param {String} oldval Old Value
57189              */
57190         "propertychange": true
57191     });
57192     this.customEditors = this.customEditors || {};
57193 };
57194 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57195     
57196      /**
57197      * @cfg {Object} customEditors map of colnames=> custom editors.
57198      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57199      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57200      * false disables editing of the field.
57201          */
57202     
57203       /**
57204      * @cfg {Object} propertyNames map of property Names to their displayed value
57205          */
57206     
57207     render : function(){
57208         Roo.grid.PropertyGrid.superclass.render.call(this);
57209         this.autoSize.defer(100, this);
57210     },
57211
57212     autoSize : function(){
57213         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57214         if(this.view){
57215             this.view.fitColumns();
57216         }
57217     },
57218
57219     onColumnResize : function(){
57220         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57221         this.autoSize();
57222     },
57223     /**
57224      * Sets the data for the Grid
57225      * accepts a Key => Value object of all the elements avaiable.
57226      * @param {Object} data  to appear in grid.
57227      */
57228     setSource : function(source){
57229         this.store.setSource(source);
57230         //this.autoSize();
57231     },
57232     /**
57233      * Gets all the data from the grid.
57234      * @return {Object} data  data stored in grid
57235      */
57236     getSource : function(){
57237         return this.store.getSource();
57238     }
57239 });/*
57240   
57241  * Licence LGPL
57242  
57243  */
57244  
57245 /**
57246  * @class Roo.grid.Calendar
57247  * @extends Roo.util.Grid
57248  * This class extends the Grid to provide a calendar widget
57249  * <br><br>Usage:<pre><code>
57250  var grid = new Roo.grid.Calendar("my-container-id", {
57251      ds: myDataStore,
57252      cm: myColModel,
57253      selModel: mySelectionModel,
57254      autoSizeColumns: true,
57255      monitorWindowResize: false,
57256      trackMouseOver: true
57257      eventstore : real data store..
57258  });
57259  // set any options
57260  grid.render();
57261   
57262   * @constructor
57263  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57264  * The container MUST have some type of size defined for the grid to fill. The container will be
57265  * automatically set to position relative if it isn't already.
57266  * @param {Object} config A config object that sets properties on this grid.
57267  */
57268 Roo.grid.Calendar = function(container, config){
57269         // initialize the container
57270         this.container = Roo.get(container);
57271         this.container.update("");
57272         this.container.setStyle("overflow", "hidden");
57273     this.container.addClass('x-grid-container');
57274
57275     this.id = this.container.id;
57276
57277     Roo.apply(this, config);
57278     // check and correct shorthanded configs
57279     
57280     var rows = [];
57281     var d =1;
57282     for (var r = 0;r < 6;r++) {
57283         
57284         rows[r]=[];
57285         for (var c =0;c < 7;c++) {
57286             rows[r][c]= '';
57287         }
57288     }
57289     if (this.eventStore) {
57290         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57291         this.eventStore.on('load',this.onLoad, this);
57292         this.eventStore.on('beforeload',this.clearEvents, this);
57293          
57294     }
57295     
57296     this.dataSource = new Roo.data.Store({
57297             proxy: new Roo.data.MemoryProxy(rows),
57298             reader: new Roo.data.ArrayReader({}, [
57299                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57300     });
57301
57302     this.dataSource.load();
57303     this.ds = this.dataSource;
57304     this.ds.xmodule = this.xmodule || false;
57305     
57306     
57307     var cellRender = function(v,x,r)
57308     {
57309         return String.format(
57310             '<div class="fc-day  fc-widget-content"><div>' +
57311                 '<div class="fc-event-container"></div>' +
57312                 '<div class="fc-day-number">{0}</div>'+
57313                 
57314                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57315             '</div></div>', v);
57316     
57317     }
57318     
57319     
57320     this.colModel = new Roo.grid.ColumnModel( [
57321         {
57322             xtype: 'ColumnModel',
57323             xns: Roo.grid,
57324             dataIndex : 'weekday0',
57325             header : 'Sunday',
57326             renderer : cellRender
57327         },
57328         {
57329             xtype: 'ColumnModel',
57330             xns: Roo.grid,
57331             dataIndex : 'weekday1',
57332             header : 'Monday',
57333             renderer : cellRender
57334         },
57335         {
57336             xtype: 'ColumnModel',
57337             xns: Roo.grid,
57338             dataIndex : 'weekday2',
57339             header : 'Tuesday',
57340             renderer : cellRender
57341         },
57342         {
57343             xtype: 'ColumnModel',
57344             xns: Roo.grid,
57345             dataIndex : 'weekday3',
57346             header : 'Wednesday',
57347             renderer : cellRender
57348         },
57349         {
57350             xtype: 'ColumnModel',
57351             xns: Roo.grid,
57352             dataIndex : 'weekday4',
57353             header : 'Thursday',
57354             renderer : cellRender
57355         },
57356         {
57357             xtype: 'ColumnModel',
57358             xns: Roo.grid,
57359             dataIndex : 'weekday5',
57360             header : 'Friday',
57361             renderer : cellRender
57362         },
57363         {
57364             xtype: 'ColumnModel',
57365             xns: Roo.grid,
57366             dataIndex : 'weekday6',
57367             header : 'Saturday',
57368             renderer : cellRender
57369         }
57370     ]);
57371     this.cm = this.colModel;
57372     this.cm.xmodule = this.xmodule || false;
57373  
57374         
57375           
57376     //this.selModel = new Roo.grid.CellSelectionModel();
57377     //this.sm = this.selModel;
57378     //this.selModel.init(this);
57379     
57380     
57381     if(this.width){
57382         this.container.setWidth(this.width);
57383     }
57384
57385     if(this.height){
57386         this.container.setHeight(this.height);
57387     }
57388     /** @private */
57389         this.addEvents({
57390         // raw events
57391         /**
57392          * @event click
57393          * The raw click event for the entire grid.
57394          * @param {Roo.EventObject} e
57395          */
57396         "click" : true,
57397         /**
57398          * @event dblclick
57399          * The raw dblclick event for the entire grid.
57400          * @param {Roo.EventObject} e
57401          */
57402         "dblclick" : true,
57403         /**
57404          * @event contextmenu
57405          * The raw contextmenu event for the entire grid.
57406          * @param {Roo.EventObject} e
57407          */
57408         "contextmenu" : true,
57409         /**
57410          * @event mousedown
57411          * The raw mousedown event for the entire grid.
57412          * @param {Roo.EventObject} e
57413          */
57414         "mousedown" : true,
57415         /**
57416          * @event mouseup
57417          * The raw mouseup event for the entire grid.
57418          * @param {Roo.EventObject} e
57419          */
57420         "mouseup" : true,
57421         /**
57422          * @event mouseover
57423          * The raw mouseover event for the entire grid.
57424          * @param {Roo.EventObject} e
57425          */
57426         "mouseover" : true,
57427         /**
57428          * @event mouseout
57429          * The raw mouseout event for the entire grid.
57430          * @param {Roo.EventObject} e
57431          */
57432         "mouseout" : true,
57433         /**
57434          * @event keypress
57435          * The raw keypress event for the entire grid.
57436          * @param {Roo.EventObject} e
57437          */
57438         "keypress" : true,
57439         /**
57440          * @event keydown
57441          * The raw keydown event for the entire grid.
57442          * @param {Roo.EventObject} e
57443          */
57444         "keydown" : true,
57445
57446         // custom events
57447
57448         /**
57449          * @event cellclick
57450          * Fires when a cell is clicked
57451          * @param {Grid} this
57452          * @param {Number} rowIndex
57453          * @param {Number} columnIndex
57454          * @param {Roo.EventObject} e
57455          */
57456         "cellclick" : true,
57457         /**
57458          * @event celldblclick
57459          * Fires when a cell is double clicked
57460          * @param {Grid} this
57461          * @param {Number} rowIndex
57462          * @param {Number} columnIndex
57463          * @param {Roo.EventObject} e
57464          */
57465         "celldblclick" : true,
57466         /**
57467          * @event rowclick
57468          * Fires when a row is clicked
57469          * @param {Grid} this
57470          * @param {Number} rowIndex
57471          * @param {Roo.EventObject} e
57472          */
57473         "rowclick" : true,
57474         /**
57475          * @event rowdblclick
57476          * Fires when a row is double clicked
57477          * @param {Grid} this
57478          * @param {Number} rowIndex
57479          * @param {Roo.EventObject} e
57480          */
57481         "rowdblclick" : true,
57482         /**
57483          * @event headerclick
57484          * Fires when a header is clicked
57485          * @param {Grid} this
57486          * @param {Number} columnIndex
57487          * @param {Roo.EventObject} e
57488          */
57489         "headerclick" : true,
57490         /**
57491          * @event headerdblclick
57492          * Fires when a header cell is double clicked
57493          * @param {Grid} this
57494          * @param {Number} columnIndex
57495          * @param {Roo.EventObject} e
57496          */
57497         "headerdblclick" : true,
57498         /**
57499          * @event rowcontextmenu
57500          * Fires when a row is right clicked
57501          * @param {Grid} this
57502          * @param {Number} rowIndex
57503          * @param {Roo.EventObject} e
57504          */
57505         "rowcontextmenu" : true,
57506         /**
57507          * @event cellcontextmenu
57508          * Fires when a cell is right clicked
57509          * @param {Grid} this
57510          * @param {Number} rowIndex
57511          * @param {Number} cellIndex
57512          * @param {Roo.EventObject} e
57513          */
57514          "cellcontextmenu" : true,
57515         /**
57516          * @event headercontextmenu
57517          * Fires when a header is right clicked
57518          * @param {Grid} this
57519          * @param {Number} columnIndex
57520          * @param {Roo.EventObject} e
57521          */
57522         "headercontextmenu" : true,
57523         /**
57524          * @event bodyscroll
57525          * Fires when the body element is scrolled
57526          * @param {Number} scrollLeft
57527          * @param {Number} scrollTop
57528          */
57529         "bodyscroll" : true,
57530         /**
57531          * @event columnresize
57532          * Fires when the user resizes a column
57533          * @param {Number} columnIndex
57534          * @param {Number} newSize
57535          */
57536         "columnresize" : true,
57537         /**
57538          * @event columnmove
57539          * Fires when the user moves a column
57540          * @param {Number} oldIndex
57541          * @param {Number} newIndex
57542          */
57543         "columnmove" : true,
57544         /**
57545          * @event startdrag
57546          * Fires when row(s) start being dragged
57547          * @param {Grid} this
57548          * @param {Roo.GridDD} dd The drag drop object
57549          * @param {event} e The raw browser event
57550          */
57551         "startdrag" : true,
57552         /**
57553          * @event enddrag
57554          * Fires when a drag operation is complete
57555          * @param {Grid} this
57556          * @param {Roo.GridDD} dd The drag drop object
57557          * @param {event} e The raw browser event
57558          */
57559         "enddrag" : true,
57560         /**
57561          * @event dragdrop
57562          * Fires when dragged row(s) are dropped on a valid DD target
57563          * @param {Grid} this
57564          * @param {Roo.GridDD} dd The drag drop object
57565          * @param {String} targetId The target drag drop object
57566          * @param {event} e The raw browser event
57567          */
57568         "dragdrop" : true,
57569         /**
57570          * @event dragover
57571          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57572          * @param {Grid} this
57573          * @param {Roo.GridDD} dd The drag drop object
57574          * @param {String} targetId The target drag drop object
57575          * @param {event} e The raw browser event
57576          */
57577         "dragover" : true,
57578         /**
57579          * @event dragenter
57580          *  Fires when the dragged row(s) first cross another DD target while being dragged
57581          * @param {Grid} this
57582          * @param {Roo.GridDD} dd The drag drop object
57583          * @param {String} targetId The target drag drop object
57584          * @param {event} e The raw browser event
57585          */
57586         "dragenter" : true,
57587         /**
57588          * @event dragout
57589          * Fires when the dragged row(s) leave another DD target while being dragged
57590          * @param {Grid} this
57591          * @param {Roo.GridDD} dd The drag drop object
57592          * @param {String} targetId The target drag drop object
57593          * @param {event} e The raw browser event
57594          */
57595         "dragout" : true,
57596         /**
57597          * @event rowclass
57598          * Fires when a row is rendered, so you can change add a style to it.
57599          * @param {GridView} gridview   The grid view
57600          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57601          */
57602         'rowclass' : true,
57603
57604         /**
57605          * @event render
57606          * Fires when the grid is rendered
57607          * @param {Grid} grid
57608          */
57609         'render' : true,
57610             /**
57611              * @event select
57612              * Fires when a date is selected
57613              * @param {DatePicker} this
57614              * @param {Date} date The selected date
57615              */
57616         'select': true,
57617         /**
57618              * @event monthchange
57619              * Fires when the displayed month changes 
57620              * @param {DatePicker} this
57621              * @param {Date} date The selected month
57622              */
57623         'monthchange': true,
57624         /**
57625              * @event evententer
57626              * Fires when mouse over an event
57627              * @param {Calendar} this
57628              * @param {event} Event
57629              */
57630         'evententer': true,
57631         /**
57632              * @event eventleave
57633              * Fires when the mouse leaves an
57634              * @param {Calendar} this
57635              * @param {event}
57636              */
57637         'eventleave': true,
57638         /**
57639              * @event eventclick
57640              * Fires when the mouse click an
57641              * @param {Calendar} this
57642              * @param {event}
57643              */
57644         'eventclick': true,
57645         /**
57646              * @event eventrender
57647              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57648              * @param {Calendar} this
57649              * @param {data} data to be modified
57650              */
57651         'eventrender': true
57652         
57653     });
57654
57655     Roo.grid.Grid.superclass.constructor.call(this);
57656     this.on('render', function() {
57657         this.view.el.addClass('x-grid-cal'); 
57658         
57659         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57660
57661     },this);
57662     
57663     if (!Roo.grid.Calendar.style) {
57664         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57665             
57666             
57667             '.x-grid-cal .x-grid-col' :  {
57668                 height: 'auto !important',
57669                 'vertical-align': 'top'
57670             },
57671             '.x-grid-cal  .fc-event-hori' : {
57672                 height: '14px'
57673             }
57674              
57675             
57676         }, Roo.id());
57677     }
57678
57679     
57680     
57681 };
57682 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57683     /**
57684      * @cfg {Store} eventStore The store that loads events.
57685      */
57686     eventStore : 25,
57687
57688      
57689     activeDate : false,
57690     startDay : 0,
57691     autoWidth : true,
57692     monitorWindowResize : false,
57693
57694     
57695     resizeColumns : function() {
57696         var col = (this.view.el.getWidth() / 7) - 3;
57697         // loop through cols, and setWidth
57698         for(var i =0 ; i < 7 ; i++){
57699             this.cm.setColumnWidth(i, col);
57700         }
57701     },
57702      setDate :function(date) {
57703         
57704         Roo.log('setDate?');
57705         
57706         this.resizeColumns();
57707         var vd = this.activeDate;
57708         this.activeDate = date;
57709 //        if(vd && this.el){
57710 //            var t = date.getTime();
57711 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57712 //                Roo.log('using add remove');
57713 //                
57714 //                this.fireEvent('monthchange', this, date);
57715 //                
57716 //                this.cells.removeClass("fc-state-highlight");
57717 //                this.cells.each(function(c){
57718 //                   if(c.dateValue == t){
57719 //                       c.addClass("fc-state-highlight");
57720 //                       setTimeout(function(){
57721 //                            try{c.dom.firstChild.focus();}catch(e){}
57722 //                       }, 50);
57723 //                       return false;
57724 //                   }
57725 //                   return true;
57726 //                });
57727 //                return;
57728 //            }
57729 //        }
57730         
57731         var days = date.getDaysInMonth();
57732         
57733         var firstOfMonth = date.getFirstDateOfMonth();
57734         var startingPos = firstOfMonth.getDay()-this.startDay;
57735         
57736         if(startingPos < this.startDay){
57737             startingPos += 7;
57738         }
57739         
57740         var pm = date.add(Date.MONTH, -1);
57741         var prevStart = pm.getDaysInMonth()-startingPos;
57742 //        
57743         
57744         
57745         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57746         
57747         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57748         //this.cells.addClassOnOver('fc-state-hover');
57749         
57750         var cells = this.cells.elements;
57751         var textEls = this.textNodes;
57752         
57753         //Roo.each(cells, function(cell){
57754         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57755         //});
57756         
57757         days += startingPos;
57758
57759         // convert everything to numbers so it's fast
57760         var day = 86400000;
57761         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57762         //Roo.log(d);
57763         //Roo.log(pm);
57764         //Roo.log(prevStart);
57765         
57766         var today = new Date().clearTime().getTime();
57767         var sel = date.clearTime().getTime();
57768         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57769         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57770         var ddMatch = this.disabledDatesRE;
57771         var ddText = this.disabledDatesText;
57772         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57773         var ddaysText = this.disabledDaysText;
57774         var format = this.format;
57775         
57776         var setCellClass = function(cal, cell){
57777             
57778             //Roo.log('set Cell Class');
57779             cell.title = "";
57780             var t = d.getTime();
57781             
57782             //Roo.log(d);
57783             
57784             
57785             cell.dateValue = t;
57786             if(t == today){
57787                 cell.className += " fc-today";
57788                 cell.className += " fc-state-highlight";
57789                 cell.title = cal.todayText;
57790             }
57791             if(t == sel){
57792                 // disable highlight in other month..
57793                 cell.className += " fc-state-highlight";
57794                 
57795             }
57796             // disabling
57797             if(t < min) {
57798                 //cell.className = " fc-state-disabled";
57799                 cell.title = cal.minText;
57800                 return;
57801             }
57802             if(t > max) {
57803                 //cell.className = " fc-state-disabled";
57804                 cell.title = cal.maxText;
57805                 return;
57806             }
57807             if(ddays){
57808                 if(ddays.indexOf(d.getDay()) != -1){
57809                     // cell.title = ddaysText;
57810                    // cell.className = " fc-state-disabled";
57811                 }
57812             }
57813             if(ddMatch && format){
57814                 var fvalue = d.dateFormat(format);
57815                 if(ddMatch.test(fvalue)){
57816                     cell.title = ddText.replace("%0", fvalue);
57817                    cell.className = " fc-state-disabled";
57818                 }
57819             }
57820             
57821             if (!cell.initialClassName) {
57822                 cell.initialClassName = cell.dom.className;
57823             }
57824             
57825             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57826         };
57827
57828         var i = 0;
57829         
57830         for(; i < startingPos; i++) {
57831             cells[i].dayName =  (++prevStart);
57832             Roo.log(textEls[i]);
57833             d.setDate(d.getDate()+1);
57834             
57835             //cells[i].className = "fc-past fc-other-month";
57836             setCellClass(this, cells[i]);
57837         }
57838         
57839         var intDay = 0;
57840         
57841         for(; i < days; i++){
57842             intDay = i - startingPos + 1;
57843             cells[i].dayName =  (intDay);
57844             d.setDate(d.getDate()+1);
57845             
57846             cells[i].className = ''; // "x-date-active";
57847             setCellClass(this, cells[i]);
57848         }
57849         var extraDays = 0;
57850         
57851         for(; i < 42; i++) {
57852             //textEls[i].innerHTML = (++extraDays);
57853             
57854             d.setDate(d.getDate()+1);
57855             cells[i].dayName = (++extraDays);
57856             cells[i].className = "fc-future fc-other-month";
57857             setCellClass(this, cells[i]);
57858         }
57859         
57860         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57861         
57862         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57863         
57864         // this will cause all the cells to mis
57865         var rows= [];
57866         var i =0;
57867         for (var r = 0;r < 6;r++) {
57868             for (var c =0;c < 7;c++) {
57869                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57870             }    
57871         }
57872         
57873         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57874         for(i=0;i<cells.length;i++) {
57875             
57876             this.cells.elements[i].dayName = cells[i].dayName ;
57877             this.cells.elements[i].className = cells[i].className;
57878             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57879             this.cells.elements[i].title = cells[i].title ;
57880             this.cells.elements[i].dateValue = cells[i].dateValue ;
57881         }
57882         
57883         
57884         
57885         
57886         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57887         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57888         
57889         ////if(totalRows != 6){
57890             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57891            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57892        // }
57893         
57894         this.fireEvent('monthchange', this, date);
57895         
57896         
57897     },
57898  /**
57899      * Returns the grid's SelectionModel.
57900      * @return {SelectionModel}
57901      */
57902     getSelectionModel : function(){
57903         if(!this.selModel){
57904             this.selModel = new Roo.grid.CellSelectionModel();
57905         }
57906         return this.selModel;
57907     },
57908
57909     load: function() {
57910         this.eventStore.load()
57911         
57912         
57913         
57914     },
57915     
57916     findCell : function(dt) {
57917         dt = dt.clearTime().getTime();
57918         var ret = false;
57919         this.cells.each(function(c){
57920             //Roo.log("check " +c.dateValue + '?=' + dt);
57921             if(c.dateValue == dt){
57922                 ret = c;
57923                 return false;
57924             }
57925             return true;
57926         });
57927         
57928         return ret;
57929     },
57930     
57931     findCells : function(rec) {
57932         var s = rec.data.start_dt.clone().clearTime().getTime();
57933        // Roo.log(s);
57934         var e= rec.data.end_dt.clone().clearTime().getTime();
57935        // Roo.log(e);
57936         var ret = [];
57937         this.cells.each(function(c){
57938              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57939             
57940             if(c.dateValue > e){
57941                 return ;
57942             }
57943             if(c.dateValue < s){
57944                 return ;
57945             }
57946             ret.push(c);
57947         });
57948         
57949         return ret;    
57950     },
57951     
57952     findBestRow: function(cells)
57953     {
57954         var ret = 0;
57955         
57956         for (var i =0 ; i < cells.length;i++) {
57957             ret  = Math.max(cells[i].rows || 0,ret);
57958         }
57959         return ret;
57960         
57961     },
57962     
57963     
57964     addItem : function(rec)
57965     {
57966         // look for vertical location slot in
57967         var cells = this.findCells(rec);
57968         
57969         rec.row = this.findBestRow(cells);
57970         
57971         // work out the location.
57972         
57973         var crow = false;
57974         var rows = [];
57975         for(var i =0; i < cells.length; i++) {
57976             if (!crow) {
57977                 crow = {
57978                     start : cells[i],
57979                     end :  cells[i]
57980                 };
57981                 continue;
57982             }
57983             if (crow.start.getY() == cells[i].getY()) {
57984                 // on same row.
57985                 crow.end = cells[i];
57986                 continue;
57987             }
57988             // different row.
57989             rows.push(crow);
57990             crow = {
57991                 start: cells[i],
57992                 end : cells[i]
57993             };
57994             
57995         }
57996         
57997         rows.push(crow);
57998         rec.els = [];
57999         rec.rows = rows;
58000         rec.cells = cells;
58001         for (var i = 0; i < cells.length;i++) {
58002             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
58003             
58004         }
58005         
58006         
58007     },
58008     
58009     clearEvents: function() {
58010         
58011         if (!this.eventStore.getCount()) {
58012             return;
58013         }
58014         // reset number of rows in cells.
58015         Roo.each(this.cells.elements, function(c){
58016             c.rows = 0;
58017         });
58018         
58019         this.eventStore.each(function(e) {
58020             this.clearEvent(e);
58021         },this);
58022         
58023     },
58024     
58025     clearEvent : function(ev)
58026     {
58027         if (ev.els) {
58028             Roo.each(ev.els, function(el) {
58029                 el.un('mouseenter' ,this.onEventEnter, this);
58030                 el.un('mouseleave' ,this.onEventLeave, this);
58031                 el.remove();
58032             },this);
58033             ev.els = [];
58034         }
58035     },
58036     
58037     
58038     renderEvent : function(ev,ctr) {
58039         if (!ctr) {
58040              ctr = this.view.el.select('.fc-event-container',true).first();
58041         }
58042         
58043          
58044         this.clearEvent(ev);
58045             //code
58046        
58047         
58048         
58049         ev.els = [];
58050         var cells = ev.cells;
58051         var rows = ev.rows;
58052         this.fireEvent('eventrender', this, ev);
58053         
58054         for(var i =0; i < rows.length; i++) {
58055             
58056             cls = '';
58057             if (i == 0) {
58058                 cls += ' fc-event-start';
58059             }
58060             if ((i+1) == rows.length) {
58061                 cls += ' fc-event-end';
58062             }
58063             
58064             //Roo.log(ev.data);
58065             // how many rows should it span..
58066             var cg = this.eventTmpl.append(ctr,Roo.apply({
58067                 fccls : cls
58068                 
58069             }, ev.data) , true);
58070             
58071             
58072             cg.on('mouseenter' ,this.onEventEnter, this, ev);
58073             cg.on('mouseleave' ,this.onEventLeave, this, ev);
58074             cg.on('click', this.onEventClick, this, ev);
58075             
58076             ev.els.push(cg);
58077             
58078             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
58079             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
58080             //Roo.log(cg);
58081              
58082             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
58083             cg.setWidth(ebox.right - sbox.x -2);
58084         }
58085     },
58086     
58087     renderEvents: function()
58088     {   
58089         // first make sure there is enough space..
58090         
58091         if (!this.eventTmpl) {
58092             this.eventTmpl = new Roo.Template(
58093                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
58094                     '<div class="fc-event-inner">' +
58095                         '<span class="fc-event-time">{time}</span>' +
58096                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
58097                     '</div>' +
58098                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
58099                 '</div>'
58100             );
58101                 
58102         }
58103                
58104         
58105         
58106         this.cells.each(function(c) {
58107             //Roo.log(c.select('.fc-day-content div',true).first());
58108             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
58109         });
58110         
58111         var ctr = this.view.el.select('.fc-event-container',true).first();
58112         
58113         var cls;
58114         this.eventStore.each(function(ev){
58115             
58116             this.renderEvent(ev);
58117              
58118              
58119         }, this);
58120         this.view.layout();
58121         
58122     },
58123     
58124     onEventEnter: function (e, el,event,d) {
58125         this.fireEvent('evententer', this, el, event);
58126     },
58127     
58128     onEventLeave: function (e, el,event,d) {
58129         this.fireEvent('eventleave', this, el, event);
58130     },
58131     
58132     onEventClick: function (e, el,event,d) {
58133         this.fireEvent('eventclick', this, el, event);
58134     },
58135     
58136     onMonthChange: function () {
58137         this.store.load();
58138     },
58139     
58140     onLoad: function () {
58141         
58142         //Roo.log('calendar onload');
58143 //         
58144         if(this.eventStore.getCount() > 0){
58145             
58146            
58147             
58148             this.eventStore.each(function(d){
58149                 
58150                 
58151                 // FIXME..
58152                 var add =   d.data;
58153                 if (typeof(add.end_dt) == 'undefined')  {
58154                     Roo.log("Missing End time in calendar data: ");
58155                     Roo.log(d);
58156                     return;
58157                 }
58158                 if (typeof(add.start_dt) == 'undefined')  {
58159                     Roo.log("Missing Start time in calendar data: ");
58160                     Roo.log(d);
58161                     return;
58162                 }
58163                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58164                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58165                 add.id = add.id || d.id;
58166                 add.title = add.title || '??';
58167                 
58168                 this.addItem(d);
58169                 
58170              
58171             },this);
58172         }
58173         
58174         this.renderEvents();
58175     }
58176     
58177
58178 });
58179 /*
58180  grid : {
58181                 xtype: 'Grid',
58182                 xns: Roo.grid,
58183                 listeners : {
58184                     render : function ()
58185                     {
58186                         _this.grid = this;
58187                         
58188                         if (!this.view.el.hasClass('course-timesheet')) {
58189                             this.view.el.addClass('course-timesheet');
58190                         }
58191                         if (this.tsStyle) {
58192                             this.ds.load({});
58193                             return; 
58194                         }
58195                         Roo.log('width');
58196                         Roo.log(_this.grid.view.el.getWidth());
58197                         
58198                         
58199                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58200                             '.course-timesheet .x-grid-row' : {
58201                                 height: '80px'
58202                             },
58203                             '.x-grid-row td' : {
58204                                 'vertical-align' : 0
58205                             },
58206                             '.course-edit-link' : {
58207                                 'color' : 'blue',
58208                                 'text-overflow' : 'ellipsis',
58209                                 'overflow' : 'hidden',
58210                                 'white-space' : 'nowrap',
58211                                 'cursor' : 'pointer'
58212                             },
58213                             '.sub-link' : {
58214                                 'color' : 'green'
58215                             },
58216                             '.de-act-sup-link' : {
58217                                 'color' : 'purple',
58218                                 'text-decoration' : 'line-through'
58219                             },
58220                             '.de-act-link' : {
58221                                 'color' : 'red',
58222                                 'text-decoration' : 'line-through'
58223                             },
58224                             '.course-timesheet .course-highlight' : {
58225                                 'border-top-style': 'dashed !important',
58226                                 'border-bottom-bottom': 'dashed !important'
58227                             },
58228                             '.course-timesheet .course-item' : {
58229                                 'font-family'   : 'tahoma, arial, helvetica',
58230                                 'font-size'     : '11px',
58231                                 'overflow'      : 'hidden',
58232                                 'padding-left'  : '10px',
58233                                 'padding-right' : '10px',
58234                                 'padding-top' : '10px' 
58235                             }
58236                             
58237                         }, Roo.id());
58238                                 this.ds.load({});
58239                     }
58240                 },
58241                 autoWidth : true,
58242                 monitorWindowResize : false,
58243                 cellrenderer : function(v,x,r)
58244                 {
58245                     return v;
58246                 },
58247                 sm : {
58248                     xtype: 'CellSelectionModel',
58249                     xns: Roo.grid
58250                 },
58251                 dataSource : {
58252                     xtype: 'Store',
58253                     xns: Roo.data,
58254                     listeners : {
58255                         beforeload : function (_self, options)
58256                         {
58257                             options.params = options.params || {};
58258                             options.params._month = _this.monthField.getValue();
58259                             options.params.limit = 9999;
58260                             options.params['sort'] = 'when_dt';    
58261                             options.params['dir'] = 'ASC';    
58262                             this.proxy.loadResponse = this.loadResponse;
58263                             Roo.log("load?");
58264                             //this.addColumns();
58265                         },
58266                         load : function (_self, records, options)
58267                         {
58268                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58269                                 // if you click on the translation.. you can edit it...
58270                                 var el = Roo.get(this);
58271                                 var id = el.dom.getAttribute('data-id');
58272                                 var d = el.dom.getAttribute('data-date');
58273                                 var t = el.dom.getAttribute('data-time');
58274                                 //var id = this.child('span').dom.textContent;
58275                                 
58276                                 //Roo.log(this);
58277                                 Pman.Dialog.CourseCalendar.show({
58278                                     id : id,
58279                                     when_d : d,
58280                                     when_t : t,
58281                                     productitem_active : id ? 1 : 0
58282                                 }, function() {
58283                                     _this.grid.ds.load({});
58284                                 });
58285                            
58286                            });
58287                            
58288                            _this.panel.fireEvent('resize', [ '', '' ]);
58289                         }
58290                     },
58291                     loadResponse : function(o, success, response){
58292                             // this is overridden on before load..
58293                             
58294                             Roo.log("our code?");       
58295                             //Roo.log(success);
58296                             //Roo.log(response)
58297                             delete this.activeRequest;
58298                             if(!success){
58299                                 this.fireEvent("loadexception", this, o, response);
58300                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58301                                 return;
58302                             }
58303                             var result;
58304                             try {
58305                                 result = o.reader.read(response);
58306                             }catch(e){
58307                                 Roo.log("load exception?");
58308                                 this.fireEvent("loadexception", this, o, response, e);
58309                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58310                                 return;
58311                             }
58312                             Roo.log("ready...");        
58313                             // loop through result.records;
58314                             // and set this.tdate[date] = [] << array of records..
58315                             _this.tdata  = {};
58316                             Roo.each(result.records, function(r){
58317                                 //Roo.log(r.data);
58318                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58319                                     _this.tdata[r.data.when_dt.format('j')] = [];
58320                                 }
58321                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58322                             });
58323                             
58324                             //Roo.log(_this.tdata);
58325                             
58326                             result.records = [];
58327                             result.totalRecords = 6;
58328                     
58329                             // let's generate some duumy records for the rows.
58330                             //var st = _this.dateField.getValue();
58331                             
58332                             // work out monday..
58333                             //st = st.add(Date.DAY, -1 * st.format('w'));
58334                             
58335                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58336                             
58337                             var firstOfMonth = date.getFirstDayOfMonth();
58338                             var days = date.getDaysInMonth();
58339                             var d = 1;
58340                             var firstAdded = false;
58341                             for (var i = 0; i < result.totalRecords ; i++) {
58342                                 //var d= st.add(Date.DAY, i);
58343                                 var row = {};
58344                                 var added = 0;
58345                                 for(var w = 0 ; w < 7 ; w++){
58346                                     if(!firstAdded && firstOfMonth != w){
58347                                         continue;
58348                                     }
58349                                     if(d > days){
58350                                         continue;
58351                                     }
58352                                     firstAdded = true;
58353                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58354                                     row['weekday'+w] = String.format(
58355                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58356                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58357                                                     d,
58358                                                     date.format('Y-m-')+dd
58359                                                 );
58360                                     added++;
58361                                     if(typeof(_this.tdata[d]) != 'undefined'){
58362                                         Roo.each(_this.tdata[d], function(r){
58363                                             var is_sub = '';
58364                                             var deactive = '';
58365                                             var id = r.id;
58366                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58367                                             if(r.parent_id*1>0){
58368                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58369                                                 id = r.parent_id;
58370                                             }
58371                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58372                                                 deactive = 'de-act-link';
58373                                             }
58374                                             
58375                                             row['weekday'+w] += String.format(
58376                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58377                                                     id, //0
58378                                                     r.product_id_name, //1
58379                                                     r.when_dt.format('h:ia'), //2
58380                                                     is_sub, //3
58381                                                     deactive, //4
58382                                                     desc // 5
58383                                             );
58384                                         });
58385                                     }
58386                                     d++;
58387                                 }
58388                                 
58389                                 // only do this if something added..
58390                                 if(added > 0){ 
58391                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58392                                 }
58393                                 
58394                                 
58395                                 // push it twice. (second one with an hour..
58396                                 
58397                             }
58398                             //Roo.log(result);
58399                             this.fireEvent("load", this, o, o.request.arg);
58400                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58401                         },
58402                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58403                     proxy : {
58404                         xtype: 'HttpProxy',
58405                         xns: Roo.data,
58406                         method : 'GET',
58407                         url : baseURL + '/Roo/Shop_course.php'
58408                     },
58409                     reader : {
58410                         xtype: 'JsonReader',
58411                         xns: Roo.data,
58412                         id : 'id',
58413                         fields : [
58414                             {
58415                                 'name': 'id',
58416                                 'type': 'int'
58417                             },
58418                             {
58419                                 'name': 'when_dt',
58420                                 'type': 'string'
58421                             },
58422                             {
58423                                 'name': 'end_dt',
58424                                 'type': 'string'
58425                             },
58426                             {
58427                                 'name': 'parent_id',
58428                                 'type': 'int'
58429                             },
58430                             {
58431                                 'name': 'product_id',
58432                                 'type': 'int'
58433                             },
58434                             {
58435                                 'name': 'productitem_id',
58436                                 'type': 'int'
58437                             },
58438                             {
58439                                 'name': 'guid',
58440                                 'type': 'int'
58441                             }
58442                         ]
58443                     }
58444                 },
58445                 toolbar : {
58446                     xtype: 'Toolbar',
58447                     xns: Roo,
58448                     items : [
58449                         {
58450                             xtype: 'Button',
58451                             xns: Roo.Toolbar,
58452                             listeners : {
58453                                 click : function (_self, e)
58454                                 {
58455                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58456                                     sd.setMonth(sd.getMonth()-1);
58457                                     _this.monthField.setValue(sd.format('Y-m-d'));
58458                                     _this.grid.ds.load({});
58459                                 }
58460                             },
58461                             text : "Back"
58462                         },
58463                         {
58464                             xtype: 'Separator',
58465                             xns: Roo.Toolbar
58466                         },
58467                         {
58468                             xtype: 'MonthField',
58469                             xns: Roo.form,
58470                             listeners : {
58471                                 render : function (_self)
58472                                 {
58473                                     _this.monthField = _self;
58474                                    // _this.monthField.set  today
58475                                 },
58476                                 select : function (combo, date)
58477                                 {
58478                                     _this.grid.ds.load({});
58479                                 }
58480                             },
58481                             value : (function() { return new Date(); })()
58482                         },
58483                         {
58484                             xtype: 'Separator',
58485                             xns: Roo.Toolbar
58486                         },
58487                         {
58488                             xtype: 'TextItem',
58489                             xns: Roo.Toolbar,
58490                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58491                         },
58492                         {
58493                             xtype: 'Fill',
58494                             xns: Roo.Toolbar
58495                         },
58496                         {
58497                             xtype: 'Button',
58498                             xns: Roo.Toolbar,
58499                             listeners : {
58500                                 click : function (_self, e)
58501                                 {
58502                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58503                                     sd.setMonth(sd.getMonth()+1);
58504                                     _this.monthField.setValue(sd.format('Y-m-d'));
58505                                     _this.grid.ds.load({});
58506                                 }
58507                             },
58508                             text : "Next"
58509                         }
58510                     ]
58511                 },
58512                  
58513             }
58514         };
58515         
58516         *//*
58517  * Based on:
58518  * Ext JS Library 1.1.1
58519  * Copyright(c) 2006-2007, Ext JS, LLC.
58520  *
58521  * Originally Released Under LGPL - original licence link has changed is not relivant.
58522  *
58523  * Fork - LGPL
58524  * <script type="text/javascript">
58525  */
58526  
58527 /**
58528  * @class Roo.LoadMask
58529  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58530  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58531  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58532  * element's UpdateManager load indicator and will be destroyed after the initial load.
58533  * @constructor
58534  * Create a new LoadMask
58535  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58536  * @param {Object} config The config object
58537  */
58538 Roo.LoadMask = function(el, config){
58539     this.el = Roo.get(el);
58540     Roo.apply(this, config);
58541     if(this.store){
58542         this.store.on('beforeload', this.onBeforeLoad, this);
58543         this.store.on('load', this.onLoad, this);
58544         this.store.on('loadexception', this.onLoadException, this);
58545         this.removeMask = false;
58546     }else{
58547         var um = this.el.getUpdateManager();
58548         um.showLoadIndicator = false; // disable the default indicator
58549         um.on('beforeupdate', this.onBeforeLoad, this);
58550         um.on('update', this.onLoad, this);
58551         um.on('failure', this.onLoad, this);
58552         this.removeMask = true;
58553     }
58554 };
58555
58556 Roo.LoadMask.prototype = {
58557     /**
58558      * @cfg {Boolean} removeMask
58559      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58560      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58561      */
58562     /**
58563      * @cfg {String} msg
58564      * The text to display in a centered loading message box (defaults to 'Loading...')
58565      */
58566     msg : 'Loading...',
58567     /**
58568      * @cfg {String} msgCls
58569      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58570      */
58571     msgCls : 'x-mask-loading',
58572
58573     /**
58574      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58575      * @type Boolean
58576      */
58577     disabled: false,
58578
58579     /**
58580      * Disables the mask to prevent it from being displayed
58581      */
58582     disable : function(){
58583        this.disabled = true;
58584     },
58585
58586     /**
58587      * Enables the mask so that it can be displayed
58588      */
58589     enable : function(){
58590         this.disabled = false;
58591     },
58592     
58593     onLoadException : function()
58594     {
58595         Roo.log(arguments);
58596         
58597         if (typeof(arguments[3]) != 'undefined') {
58598             Roo.MessageBox.alert("Error loading",arguments[3]);
58599         } 
58600         /*
58601         try {
58602             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58603                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58604             }   
58605         } catch(e) {
58606             
58607         }
58608         */
58609     
58610         
58611         
58612         this.el.unmask(this.removeMask);
58613     },
58614     // private
58615     onLoad : function()
58616     {
58617         this.el.unmask(this.removeMask);
58618     },
58619
58620     // private
58621     onBeforeLoad : function(){
58622         if(!this.disabled){
58623             this.el.mask(this.msg, this.msgCls);
58624         }
58625     },
58626
58627     // private
58628     destroy : function(){
58629         if(this.store){
58630             this.store.un('beforeload', this.onBeforeLoad, this);
58631             this.store.un('load', this.onLoad, this);
58632             this.store.un('loadexception', this.onLoadException, this);
58633         }else{
58634             var um = this.el.getUpdateManager();
58635             um.un('beforeupdate', this.onBeforeLoad, this);
58636             um.un('update', this.onLoad, this);
58637             um.un('failure', this.onLoad, this);
58638         }
58639     }
58640 };/*
58641  * Based on:
58642  * Ext JS Library 1.1.1
58643  * Copyright(c) 2006-2007, Ext JS, LLC.
58644  *
58645  * Originally Released Under LGPL - original licence link has changed is not relivant.
58646  *
58647  * Fork - LGPL
58648  * <script type="text/javascript">
58649  */
58650
58651
58652 /**
58653  * @class Roo.XTemplate
58654  * @extends Roo.Template
58655  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58656 <pre><code>
58657 var t = new Roo.XTemplate(
58658         '&lt;select name="{name}"&gt;',
58659                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58660         '&lt;/select&gt;'
58661 );
58662  
58663 // then append, applying the master template values
58664  </code></pre>
58665  *
58666  * Supported features:
58667  *
58668  *  Tags:
58669
58670 <pre><code>
58671       {a_variable} - output encoded.
58672       {a_variable.format:("Y-m-d")} - call a method on the variable
58673       {a_variable:raw} - unencoded output
58674       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58675       {a_variable:this.method_on_template(...)} - call a method on the template object.
58676  
58677 </code></pre>
58678  *  The tpl tag:
58679 <pre><code>
58680         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58681         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58682         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58683         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58684   
58685         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58686         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58687 </code></pre>
58688  *      
58689  */
58690 Roo.XTemplate = function()
58691 {
58692     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58693     if (this.html) {
58694         this.compile();
58695     }
58696 };
58697
58698
58699 Roo.extend(Roo.XTemplate, Roo.Template, {
58700
58701     /**
58702      * The various sub templates
58703      */
58704     tpls : false,
58705     /**
58706      *
58707      * basic tag replacing syntax
58708      * WORD:WORD()
58709      *
58710      * // you can fake an object call by doing this
58711      *  x.t:(test,tesT) 
58712      * 
58713      */
58714     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58715
58716     /**
58717      * compile the template
58718      *
58719      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58720      *
58721      */
58722     compile: function()
58723     {
58724         var s = this.html;
58725      
58726         s = ['<tpl>', s, '</tpl>'].join('');
58727     
58728         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58729             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58730             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58731             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58732             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58733             m,
58734             id     = 0,
58735             tpls   = [];
58736     
58737         while(true == !!(m = s.match(re))){
58738             var forMatch   = m[0].match(nameRe),
58739                 ifMatch   = m[0].match(ifRe),
58740                 execMatch   = m[0].match(execRe),
58741                 namedMatch   = m[0].match(namedRe),
58742                 
58743                 exp  = null, 
58744                 fn   = null,
58745                 exec = null,
58746                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58747                 
58748             if (ifMatch) {
58749                 // if - puts fn into test..
58750                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58751                 if(exp){
58752                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58753                 }
58754             }
58755             
58756             if (execMatch) {
58757                 // exec - calls a function... returns empty if true is  returned.
58758                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58759                 if(exp){
58760                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58761                 }
58762             }
58763             
58764             
58765             if (name) {
58766                 // for = 
58767                 switch(name){
58768                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58769                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58770                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58771                 }
58772             }
58773             var uid = namedMatch ? namedMatch[1] : id;
58774             
58775             
58776             tpls.push({
58777                 id:     namedMatch ? namedMatch[1] : id,
58778                 target: name,
58779                 exec:   exec,
58780                 test:   fn,
58781                 body:   m[1] || ''
58782             });
58783             if (namedMatch) {
58784                 s = s.replace(m[0], '');
58785             } else { 
58786                 s = s.replace(m[0], '{xtpl'+ id + '}');
58787             }
58788             ++id;
58789         }
58790         this.tpls = [];
58791         for(var i = tpls.length-1; i >= 0; --i){
58792             this.compileTpl(tpls[i]);
58793             this.tpls[tpls[i].id] = tpls[i];
58794         }
58795         this.master = tpls[tpls.length-1];
58796         return this;
58797     },
58798     /**
58799      * same as applyTemplate, except it's done to one of the subTemplates
58800      * when using named templates, you can do:
58801      *
58802      * var str = pl.applySubTemplate('your-name', values);
58803      *
58804      * 
58805      * @param {Number} id of the template
58806      * @param {Object} values to apply to template
58807      * @param {Object} parent (normaly the instance of this object)
58808      */
58809     applySubTemplate : function(id, values, parent)
58810     {
58811         
58812         
58813         var t = this.tpls[id];
58814         
58815         
58816         try { 
58817             if(t.test && !t.test.call(this, values, parent)){
58818                 return '';
58819             }
58820         } catch(e) {
58821             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58822             Roo.log(e.toString());
58823             Roo.log(t.test);
58824             return ''
58825         }
58826         try { 
58827             
58828             if(t.exec && t.exec.call(this, values, parent)){
58829                 return '';
58830             }
58831         } catch(e) {
58832             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58833             Roo.log(e.toString());
58834             Roo.log(t.exec);
58835             return ''
58836         }
58837         try {
58838             var vs = t.target ? t.target.call(this, values, parent) : values;
58839             parent = t.target ? values : parent;
58840             if(t.target && vs instanceof Array){
58841                 var buf = [];
58842                 for(var i = 0, len = vs.length; i < len; i++){
58843                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58844                 }
58845                 return buf.join('');
58846             }
58847             return t.compiled.call(this, vs, parent);
58848         } catch (e) {
58849             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58850             Roo.log(e.toString());
58851             Roo.log(t.compiled);
58852             return '';
58853         }
58854     },
58855
58856     compileTpl : function(tpl)
58857     {
58858         var fm = Roo.util.Format;
58859         var useF = this.disableFormats !== true;
58860         var sep = Roo.isGecko ? "+" : ",";
58861         var undef = function(str) {
58862             Roo.log("Property not found :"  + str);
58863             return '';
58864         };
58865         
58866         var fn = function(m, name, format, args)
58867         {
58868             //Roo.log(arguments);
58869             args = args ? args.replace(/\\'/g,"'") : args;
58870             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58871             if (typeof(format) == 'undefined') {
58872                 format= 'htmlEncode';
58873             }
58874             if (format == 'raw' ) {
58875                 format = false;
58876             }
58877             
58878             if(name.substr(0, 4) == 'xtpl'){
58879                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58880             }
58881             
58882             // build an array of options to determine if value is undefined..
58883             
58884             // basically get 'xxxx.yyyy' then do
58885             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58886             //    (function () { Roo.log("Property not found"); return ''; })() :
58887             //    ......
58888             
58889             var udef_ar = [];
58890             var lookfor = '';
58891             Roo.each(name.split('.'), function(st) {
58892                 lookfor += (lookfor.length ? '.': '') + st;
58893                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58894             });
58895             
58896             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58897             
58898             
58899             if(format && useF){
58900                 
58901                 args = args ? ',' + args : "";
58902                  
58903                 if(format.substr(0, 5) != "this."){
58904                     format = "fm." + format + '(';
58905                 }else{
58906                     format = 'this.call("'+ format.substr(5) + '", ';
58907                     args = ", values";
58908                 }
58909                 
58910                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58911             }
58912              
58913             if (args.length) {
58914                 // called with xxyx.yuu:(test,test)
58915                 // change to ()
58916                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58917             }
58918             // raw.. - :raw modifier..
58919             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58920             
58921         };
58922         var body;
58923         // branched to use + in gecko and [].join() in others
58924         if(Roo.isGecko){
58925             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58926                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58927                     "';};};";
58928         }else{
58929             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58930             body.push(tpl.body.replace(/(\r\n|\n)/g,
58931                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58932             body.push("'].join('');};};");
58933             body = body.join('');
58934         }
58935         
58936         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58937        
58938         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58939         eval(body);
58940         
58941         return this;
58942     },
58943
58944     applyTemplate : function(values){
58945         return this.master.compiled.call(this, values, {});
58946         //var s = this.subs;
58947     },
58948
58949     apply : function(){
58950         return this.applyTemplate.apply(this, arguments);
58951     }
58952
58953  });
58954
58955 Roo.XTemplate.from = function(el){
58956     el = Roo.getDom(el);
58957     return new Roo.XTemplate(el.value || el.innerHTML);
58958 };